summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py3
-rw-r--r--tests/files/README.md34
-rw-r--r--tests/files/cli/1a.yaml3
-rw-r--r--tests/files/cli/1b.yaml3
-rw-r--r--tests/files/cli/2a.yaml3
-rw-r--r--tests/files/cli/2b.yaml3
-rw-r--r--tests/files/fail/test_anchor.yaml95
-rw-r--r--tests/files/fail/test_assert.yaml34
-rw-r--r--tests/files/fail/test_default.yaml21
-rw-r--r--tests/files/fail/test_desc.yaml1
-rw-r--r--tests/files/fail/test_enum.yaml16
-rw-r--r--tests/files/fail/test_example.yaml1
-rw-r--r--tests/files/fail/test_extensions.yaml0
-rw-r--r--tests/files/fail/test_func.yaml0
-rw-r--r--tests/files/fail/test_ident.yaml23
-rw-r--r--tests/files/fail/test_include.yaml0
-rw-r--r--tests/files/fail/test_length.yaml113
-rw-r--r--tests/files/fail/test_mapping.yaml186
-rw-r--r--tests/files/fail/test_matching.yaml0
-rw-r--r--tests/files/fail/test_merge.yaml37
-rw-r--r--tests/files/fail/test_name.yaml0
-rw-r--r--tests/files/fail/test_nullable.yaml19
-rw-r--r--tests/files/fail/test_pattern.yaml26
-rw-r--r--tests/files/fail/test_range.yaml219
-rw-r--r--tests/files/fail/test_required.yaml19
-rw-r--r--tests/files/fail/test_schema.yaml0
-rw-r--r--tests/files/fail/test_sequence.yaml71
-rw-r--r--tests/files/fail/test_sequence_multi.yaml31
-rw-r--r--tests/files/fail/test_type_any.yaml1
-rw-r--r--tests/files/fail/test_type_bool.yaml52
-rw-r--r--tests/files/fail/test_type_date.yaml83
-rw-r--r--tests/files/fail/test_type_float.yaml56
-rw-r--r--tests/files/fail/test_type_int.yaml45
-rw-r--r--tests/files/fail/test_type_map.yaml46
-rw-r--r--tests/files/fail/test_type_none.yaml54
-rw-r--r--tests/files/fail/test_type_number.yaml83
-rw-r--r--tests/files/fail/test_type_scalar.yaml0
-rw-r--r--tests/files/fail/test_type_seq.yaml47
-rw-r--r--tests/files/fail/test_type_str.yaml73
-rw-r--r--tests/files/fail/test_type_symbol.yaml0
-rw-r--r--tests/files/fail/test_type_text.yaml70
-rw-r--r--tests/files/fail/test_type_timestamp.yaml15
-rw-r--r--tests/files/fail/test_unique.yaml109
-rw-r--r--tests/files/fail/test_version.yaml0
-rw-r--r--tests/files/partial_schemas/1f-data.yaml1
-rw-r--r--tests/files/partial_schemas/1f-partials.yaml11
-rw-r--r--tests/files/partial_schemas/1f-schema.yaml3
-rw-r--r--tests/files/partial_schemas/1s-data.yaml1
-rw-r--r--tests/files/partial_schemas/1s-partials.yaml11
-rw-r--r--tests/files/partial_schemas/1s-schema.yaml3
-rw-r--r--tests/files/partial_schemas/2f-data.yaml1
-rw-r--r--tests/files/partial_schemas/2f-schema.yaml5
-rw-r--r--tests/files/partial_schemas/2s-data.yaml3
-rw-r--r--tests/files/partial_schemas/2s-partials.yaml16
-rw-r--r--tests/files/partial_schemas/2s-schema.yaml3
-rw-r--r--tests/files/partial_schemas/3f-data.yaml1
-rw-r--r--tests/files/partial_schemas/3f-schema.yaml3
-rw-r--r--tests/files/partial_schemas/4f-data.yaml2
-rw-r--r--tests/files/partial_schemas/4f-schema.yaml20
-rw-r--r--tests/files/partial_schemas/5f-data.yaml1
-rw-r--r--tests/files/partial_schemas/5f-schema.yaml18
-rw-r--r--tests/files/partial_schemas/6f-data.yaml4
-rw-r--r--tests/files/partial_schemas/6f-schema.yaml22
-rw-r--r--tests/files/partial_schemas/7s-data.yaml5
-rw-r--r--tests/files/partial_schemas/7s-schema.yaml12
-rw-r--r--tests/files/success/test_anchor.yaml92
-rw-r--r--tests/files/success/test_assert.yaml28
-rw-r--r--tests/files/success/test_default.yaml0
-rw-r--r--tests/files/success/test_desc.yaml7
-rw-r--r--tests/files/success/test_enum.yaml12
-rw-r--r--tests/files/success/test_example.yaml5
-rw-r--r--tests/files/success/test_extensions.yaml0
-rw-r--r--tests/files/success/test_func.yaml0
-rw-r--r--tests/files/success/test_ident.yaml22
-rw-r--r--tests/files/success/test_include.yaml0
-rw-r--r--tests/files/success/test_length.yaml98
-rw-r--r--tests/files/success/test_mapping.yaml308
-rw-r--r--tests/files/success/test_matching.yaml0
-rw-r--r--tests/files/success/test_merge.yaml36
-rw-r--r--tests/files/success/test_name.yaml0
-rw-r--r--tests/files/success/test_nullable.yaml11
-rw-r--r--tests/files/success/test_pattern.yaml18
-rw-r--r--tests/files/success/test_range.yaml166
-rw-r--r--tests/files/success/test_required.yaml15
-rw-r--r--tests/files/success/test_schema.yaml0
-rw-r--r--tests/files/success/test_sequence.yaml44
-rw-r--r--tests/files/success/test_sequence_multi.yaml64
-rw-r--r--tests/files/success/test_type_any.yaml27
-rw-r--r--tests/files/success/test_type_bool.yaml39
-rw-r--r--tests/files/success/test_type_date.yaml40
-rw-r--r--tests/files/success/test_type_enum.yaml43
-rw-r--r--tests/files/success/test_type_float.yaml43
-rw-r--r--tests/files/success/test_type_int.yaml38
-rw-r--r--tests/files/success/test_type_map.yaml39
-rw-r--r--tests/files/success/test_type_none.yaml47
-rw-r--r--tests/files/success/test_type_number.yaml76
-rw-r--r--tests/files/success/test_type_scalar.yaml76
-rw-r--r--tests/files/success/test_type_seq.yaml36
-rw-r--r--tests/files/success/test_type_str.yaml56
-rw-r--r--tests/files/success/test_type_symbol.yaml0
-rw-r--r--tests/files/success/test_type_text.yaml75
-rw-r--r--tests/files/success/test_type_timestamp.yaml37
-rw-r--r--tests/files/success/test_unique.yaml138
-rw-r--r--tests/files/success/test_version.yaml5
-rw-r--r--tests/files/unicode/1f.yaml13
-rw-r--r--tests/files/unicode/1s.yaml10
-rw-r--r--tests/files/unicode/3f.yaml10
-rw-r--r--tests/files/unicode/3s.yaml7
-rw-r--r--tests/test_cli.py62
-rw-r--r--tests/test_core.py572
-rw-r--r--tests/test_core_methods.py308
-rw-r--r--tests/test_exceptions.py29
-rw-r--r--tests/test_helper.py33
-rw-r--r--tests/test_rule.py399
-rw-r--r--tests/test_types.py75
-rw-r--r--tests/test_unicode.py137
116 files changed, 5237 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..3dbf9fa
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+""" pyKwalify validation framework """
diff --git a/tests/files/README.md b/tests/files/README.md
new file mode 100644
index 0000000..3faa375
--- /dev/null
+++ b/tests/files/README.md
@@ -0,0 +1,34 @@
+# Test files
+
+Test files are divided up into 2 types of tests. They follow a naming schema that follows `(Number)(Type).yaml` Where number is just a ever increasing integer and Type is different depending on the test type. Each type of test should be counted seperatly.
+
+- Successfull tests. Type: 's'
+- Failing tests. Type: 'f'
+
+
+
+# Successfull tests
+
+Files in `success` folder.
+
+Each file should contain a top level dict with the keys `data` and `schema` where the test data should exists.
+
+
+
+# Failing tests
+
+Files in `fail` folder.
+
+Each file should contain a top level dict with the keys `data`, `schema` and `errors` where the test data should exists.
+
+
+
+# cli tests
+
+Simple schema and data files that is used to test input of files via cli.
+
+
+
+# partial schemas
+
+Files used to test partial schema support.
diff --git a/tests/files/cli/1a.yaml b/tests/files/cli/1a.yaml
new file mode 100644
index 0000000..c34e2ee
--- /dev/null
+++ b/tests/files/cli/1a.yaml
@@ -0,0 +1,3 @@
+- foo
+- bar
+- baz
diff --git a/tests/files/cli/1b.yaml b/tests/files/cli/1b.yaml
new file mode 100644
index 0000000..41f00aa
--- /dev/null
+++ b/tests/files/cli/1b.yaml
@@ -0,0 +1,3 @@
+type: seq
+sequence:
+ - type: str
diff --git a/tests/files/cli/2a.yaml b/tests/files/cli/2a.yaml
new file mode 100644
index 0000000..a0cd6f3
--- /dev/null
+++ b/tests/files/cli/2a.yaml
@@ -0,0 +1,3 @@
+- 1
+- 2
+- 3
diff --git a/tests/files/cli/2b.yaml b/tests/files/cli/2b.yaml
new file mode 100644
index 0000000..41f00aa
--- /dev/null
+++ b/tests/files/cli/2b.yaml
@@ -0,0 +1,3 @@
+type: seq
+sequence:
+ - type: str
diff --git a/tests/files/fail/test_anchor.yaml b/tests/files/fail/test_anchor.yaml
new file mode 100644
index 0000000..dca97f5
--- /dev/null
+++ b/tests/files/fail/test_anchor.yaml
@@ -0,0 +1,95 @@
+---
+name: fail-anchor-1
+desc: schema with anchor
+schema:
+ type: seq
+ required: true
+ sequence:
+ - type: map
+ required: true
+ mapping:
+ first-name: &name
+ type: str
+ required: true
+ family-name: *name
+data:
+ - first-name: foo
+ last-name: Foo
+ - first-name: bar
+ family-name: 100
+errors:
+ - "Cannot find required key 'family-name'. Path: '/0'"
+ - "Key 'last-name' was not defined. Path: '/0'"
+ - "Value '100' is not of type 'str'. Path: '/1/family-name'"
+ ## Kwalify errors
+ # :required_nokey : 1:3:[/0] key 'family-name:' is required.
+ # :key_undefined : 2:3:[/0/last-name] key 'last-name:' is undefined.
+ # :type_unmatch : 4:3:[/1/family-name] '100': not a string.
+---
+name: fail-anchor-2
+desc: schema with anchor 2
+schema:
+ type: map
+ required: true
+ mapping:
+ title: &name
+ type: str
+ required: true
+ address-book:
+ type: seq
+ required: true
+ sequence:
+ - type: map
+ mapping:
+ name: *name
+ email:
+ type: str
+ required: true
+data:
+ title: my friends
+ address-book:
+ - name: 100
+ email: foo@mail.com
+ - first-name: bar
+ email: bar@mail.com
+errors:
+ - "Cannot find required key 'name'. Path: '/address-book/1'"
+ - "Key 'first-name' was not defined. Path: '/address-book/1'"
+ - "Value '100' is not of type 'str'. Path: '/address-book/0/name'"
+ ## Kwalify errors
+ # :type_unmatch : 3:5:[/address-book/0/name] '100': not a string.
+ # :required_nokey : 5:5:[/address-book/1] key 'name:' is required.
+ # :key_undefined : 5:5:[/address-book/1/first-name] key 'first-name:' is undefined.
+# TODO: THIS TEST IS BROKEN BECUASE IT CAUSE INFINITE RECURSION IN PYTHON
+# ---
+# name: fail-anchor-3
+# desc: document with anchor
+# schema:
+# type: seq
+# sequence:
+# - &employee
+# type: map
+# mapping:
+# name:
+# type: str
+# post:
+# type: str
+# enum:
+# - exective
+# - manager
+# - clerk
+# supervisor: *employee
+# data:
+# - &foo
+# name: 100
+# post: exective
+# supervisor: *foo
+# - &bar
+# name: foo
+# post: worker
+# supervisor: *foo
+# errors:
+# - ''
+# ## Kwalify errors
+# # :type_unmatch : 2:3:[/0/name] '100': not a string.
+# # :enum_notexist : 7:3:[/1/post] 'worker': invalid post value.
diff --git a/tests/files/fail/test_assert.yaml b/tests/files/fail/test_assert.yaml
new file mode 100644
index 0000000..9073cdf
--- /dev/null
+++ b/tests/files/fail/test_assert.yaml
@@ -0,0 +1,34 @@
+---
+name: fail-assert-1
+desc: assert test
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "less-than":
+ type: number
+ assert: val < 8
+ "more-than":
+ type: number
+ assert: 3 < val
+ "between":
+ type: number
+ assert: 3 < val and val < 8
+ "except":
+ type: number
+ assert: val < 3 or 8 < val
+data:
+ - less-than: 8
+ - more-than: 3
+ - between: 2.9
+ - except: 3.1
+errors:
+ - "Value: '2.9' assertion expression failed (3 < val and val < 8)"
+ - "Value: '3' assertion expression failed (3 < val)"
+ - "Value: '3.1' assertion expression failed (val < 3 or 8 < val)"
+ - "Value: '8' assertion expression failed (val < 8)"
+ # :assert_failed : 1:3:[/0/less-than] '8': assertion expression failed (val < 8).
+ # :assert_failed : 2:3:[/1/more-than] '3': assertion expression failed (3 < val).
+ # :assert_failed : 3:3:[/2/between] '2.9': assertion expression failed (3 < val and val < 8).
+ # :assert_failed : 4:3:[/3/except] '3.1': assertion expression failed (val < 3 or 8 < val).
diff --git a/tests/files/fail/test_default.yaml b/tests/files/fail/test_default.yaml
new file mode 100644
index 0000000..ab0a0a5
--- /dev/null
+++ b/tests/files/fail/test_default.yaml
@@ -0,0 +1,21 @@
+---
+name: fail-default-1
+desc: default value of map
+schema:
+ type: map
+ mapping:
+ =:
+ type: number
+ range:
+ min: -10
+ max: 10
+data:
+ value1: 0
+ value2: 20
+ value3: -20
+errors:
+ - "Type 'scalar' has size of '-20', less than min limit '-10'. Path: '/value3'"
+ - "Type 'scalar' has size of '20', greater than max limit '10'. Path: '/value2'"
+ ## Kwalify errors
+ # :range_toolarge : 2:1:[/value2] '20': too large (> max 10).
+ # :range_toosmall : 3:1:[/value3] '-20': too small (< min -10).
diff --git a/tests/files/fail/test_desc.yaml b/tests/files/fail/test_desc.yaml
new file mode 100644
index 0000000..401dde3
--- /dev/null
+++ b/tests/files/fail/test_desc.yaml
@@ -0,0 +1 @@
+# Becuase desc has no validation done on the value there is no failure case for this keyword
diff --git a/tests/files/fail/test_enum.yaml b/tests/files/fail/test_enum.yaml
new file mode 100644
index 0000000..9f619dc
--- /dev/null
+++ b/tests/files/fail/test_enum.yaml
@@ -0,0 +1,16 @@
+---
+name: fail-enum-1
+desc: Test simple enum
+data:
+ - A
+ - B
+ - O
+schema:
+ type: seq
+ sequence:
+ - type: str
+ enum: [E, F, G, H]
+errors:
+ - "Enum 'A' does not exist. Path: '/0'"
+ - "Enum 'B' does not exist. Path: '/1'"
+ - "Enum 'O' does not exist. Path: '/2'"
diff --git a/tests/files/fail/test_example.yaml b/tests/files/fail/test_example.yaml
new file mode 100644
index 0000000..baa8caf
--- /dev/null
+++ b/tests/files/fail/test_example.yaml
@@ -0,0 +1 @@
+# Becuase example has no validation done on the value there is no failure case for this keyword \ No newline at end of file
diff --git a/tests/files/fail/test_extensions.yaml b/tests/files/fail/test_extensions.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_extensions.yaml
diff --git a/tests/files/fail/test_func.yaml b/tests/files/fail/test_func.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_func.yaml
diff --git a/tests/files/fail/test_ident.yaml b/tests/files/fail/test_ident.yaml
new file mode 100644
index 0000000..2531dfe
--- /dev/null
+++ b/tests/files/fail/test_ident.yaml
@@ -0,0 +1,23 @@
+---
+name: fail-ident-1
+desc: ident constraint test
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ ident: true
+ "age":
+ type: int
+data:
+ - name: foo
+ age: 10
+ - name: bar
+ age: 10
+ - name: bar
+ age: 10
+errors:
+ - "Value 'bar' is not unique. Previous path: '/1/name'. Path: '/2/name'"
+ ## Kwalify errors
+ # :value_notunique : 5:3:[/2/name] 'bar': is already used at '/1/name'.
diff --git a/tests/files/fail/test_include.yaml b/tests/files/fail/test_include.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_include.yaml
diff --git a/tests/files/fail/test_length.yaml b/tests/files/fail/test_length.yaml
new file mode 100644
index 0000000..8264987
--- /dev/null
+++ b/tests/files/fail/test_length.yaml
@@ -0,0 +1,113 @@
+---
+name: fail-length-1
+desc: length test
+schema:
+ type: map
+ mapping:
+ "max-only":
+ type: seq
+ sequence:
+ - type: str
+ length: {max: 8}
+ "min-only":
+ type: seq
+ sequence:
+ - type: str
+ length: {min: 4}
+ "max-and-min":
+ type: seq
+ sequence:
+ - type: str
+ length: {max: 8, min: 4}
+data:
+ max-only:
+ - hogehoge!
+ min-only:
+ - foo
+ -
+ max-and-min:
+ - foobarbaz
+ - foo
+errors:
+ - "Value: 'foo' has length of '3', greater than min limit '4'. Path: '/max-and-min/1'"
+ - "Value: 'foo' has length of '3', greater than min limit '4'. Path: '/min-only/0'"
+ - "Value: 'foobarbaz' has length of '9', greater than max limit '8'. Path: '/max-and-min/0'"
+ - "Value: 'hogehoge!' has length of '9', greater than max limit '8'. Path: '/max-only/0'"
+ ## Kwalify errors
+ # :length_toolong : 2:3:[/max-only/0] 'hogehoge!': too long (length 9 > max 8).
+ # :length_tooshort : 4:3:[/min-only/0] 'foo': too short (length 3 < min 4).
+ # :length_toolong : 7:3:[/max-and-min/0] 'foobarbaz': too long (length 9 > max 8).
+ # :length_tooshort : 8:3:[/max-and-min/1] 'foo': too short (length 3 < min 4).
+---
+name: fail-length-2
+desc: length test (with max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "max-ex-only":
+ type: seq
+ sequence:
+ - type: str
+ length: {max-ex: 8}
+ "min-ex-only":
+ type: seq
+ sequence:
+ - type: str
+ length: {min-ex: 4}
+ "max-ex-and-min-ex":
+ type: seq
+ sequence:
+ - type: str
+ length: {max-ex: 8, min-ex: 4}
+data:
+ max-ex-only:
+ - hogehoge
+ min-ex-only:
+ - foo!
+ -
+ max-ex-and-min-ex:
+ - foobarba
+ - foo!
+errors:
+ - "Value: 'foo!' has length of '4', greater than min_ex limit '4'. Path: '/max-ex-and-min-ex/1'"
+ - "Value: 'foo!' has length of '4', greater than min_ex limit '4'. Path: '/min-ex-only/0'"
+ - "Value: 'foobarba' has length of '8', greater than max_ex limit '8'. Path: '/max-ex-and-min-ex/0'"
+ - "Value: 'hogehoge' has length of '8', greater than max_ex limit '8'. Path: '/max-ex-only/0'"
+ ## Kwalify errors
+ # :length_toolongex : 2:3:[/max-ex-only/0] 'hogehoge': too long (length 8 >= max 8).
+ # :length_tooshortex : 4:3:[/min-ex-only/0] 'foo!': too short (length 4 <= min 4).
+ # :length_toolongex : 7:3:[/max-ex-and-min-ex/0] 'foobarba': too long (length 8 >= max 8).
+ # :length_tooshortex : 8:3:[/max-ex-and-min-ex/1] 'foo!': too short (length 4 <= min 4).
+---
+name: fail-length-3
+desc: length test (with min, max, max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "A":
+ type: seq
+ sequence:
+ - type: str
+ length: {max: 8, min-ex: 4}
+ "B":
+ type: seq
+ sequence:
+ - type: str
+ length: {max-ex: 8, min: 4}
+data:
+ A:
+ - hogehoge!
+ - hoge
+ B:
+ - hogehoge
+ - hog
+errors:
+ - "Value: 'hog' has length of '3', greater than min limit '4'. Path: '/B/1'"
+ - "Value: 'hoge' has length of '4', greater than min_ex limit '4'. Path: '/A/1'"
+ - "Value: 'hogehoge!' has length of '9', greater than max limit '8'. Path: '/A/0'"
+ - "Value: 'hogehoge' has length of '8', greater than max_ex limit '8'. Path: '/B/0'"
+ ## Kwalify errors
+ # :length_toolong : 2:3:[/A/0] 'hogehoge!': too long (length 9 > max 8).
+ # :length_tooshortex : 3:3:[/A/1] 'hoge': too short (length 4 <= min 4).
+ # :length_toolongex : 5:3:[/B/0] 'hogehoge': too long (length 8 >= max 8).
+ # :length_tooshort : 6:3:[/B/1] 'hog': too short (length 3 < min 4).
diff --git a/tests/files/fail/test_mapping.yaml b/tests/files/fail/test_mapping.yaml
new file mode 100644
index 0000000..0561ea4
--- /dev/null
+++ b/tests/files/fail/test_mapping.yaml
@@ -0,0 +1,186 @@
+---
+name: fail-mapping-1
+desc: This test that typechecking works when value in map is None
+data:
+ streams:
+ - name: ~
+ sampleRateMultiple: 1
+ - name: media
+ sampleRateMultiple: 2
+schema:
+ type: map
+ mapping:
+ streams:
+ type: seq
+ required: True
+ sequence:
+ - type: map
+ mapping:
+ name:
+ type: str
+ range:
+ min: 1
+ required: True
+ sampleRateMultiple:
+ type: int
+ required: True
+errors:
+ - "required.novalue : '/streams/0/name'"
+---
+name: fail-mapping-2
+desc: Test keyword regex using default matching-rule 'any'
+data:
+ foobar1: 1
+ foobar2: 2
+ foobar3: 3
+schema:
+ type: map
+ mapping:
+ regex;(^foobar[1-2]$):
+ type: int
+errors:
+ - "Key 'foobar3' does not match any regex '^foobar[1-2]$'. Path: ''"
+---
+name: fail-mapping-3
+desc: Test keyword regex using declared matching-rule 'any'
+data:
+ foobar1: 1
+ foobar2: 2
+ bar3: 3
+schema:
+ type: map
+ matching-rule: 'any'
+ mapping:
+ regex;(^foobar):
+ type: int
+ regex;([1-2]$):
+ type: int
+errors:
+ - "Key 'bar3' does not match any regex '[1-2]$' or '^foobar'. Path: ''"
+---
+name: fail-mapping-4
+desc: Test keyword regex using declared matching-rule 'all'
+data:
+ foobar1: 1
+ foobar2: 2
+ foobar3: 3
+schema:
+ type: map
+ matching-rule: 'all'
+ mapping:
+ regex;(^foobar.*$):
+ type: int
+ regex;(^.*[1-2]$):
+ type: int
+errors:
+ - "Key 'foobar3' does not match all regex '^.*[1-2]$' and '^foobar.*$'. Path: ''"
+---
+name: fail-mapping-5
+desc: Test that sequence of mappings check the correct type and raises correct error when value is not a dict
+data:
+ - foo: whatever
+ - "sgdf"
+ - 2
+ - ~
+schema:
+ type: seq
+ required: True
+ matching: all
+ seq:
+ - type: map
+ required: True
+ map:
+ foo:
+ type: str
+errors:
+ - "Value '2' is not a dict. Value path: '/2'"
+ - "Value 'sgdf' is not a dict. Value path: '/1'"
+ - "required.novalue : '/3'"
+---
+name: fail-mapping-6
+desc: Test that type checking of mapping is done even if the mapping keyword is not specefied in the schema
+data:
+ - not
+ - a
+ - map
+schema:
+ type: map
+ allowempty: True
+errors:
+ - "Value '['not', 'a', 'map']' is not a dict. Value path: ''"
+---
+name: fail-mapping-7
+desc: Test that default mode fails out in a similar way to regular mode and that a key that is not defined when default is set uses the default impl
+data:
+ OWNERSHIP: abc
+ WHT: def
+schema:
+ type: map
+ mapping:
+ WHT:
+ type: int
+ =:
+ type: int
+errors:
+ - "Value 'abc' is not of type 'int'. Path: '/OWNERSHIP'"
+ - "Value 'def' is not of type 'int'. Path: '/WHT'"
+---
+name: fail-mapping-8
+desc: mapping test
+schema:
+ type: map
+ required: true
+ mapping:
+ name:
+ type: str
+ required: true
+ email:
+ type: str
+ # This pattern value was modified from /@/ to .+@.+ to make it copmatible with python
+ pattern: .+@.+
+ required: True
+ age:
+ type: int
+ blood:
+ type: str
+ enum:
+ - A
+ - B
+ - O
+ - AB
+ birth:
+ type: date
+data:
+ nam: foo
+ email: foo(at)mail.com
+ age: twenty
+ blood: ab
+ birth: Jul 01, 1985
+errors:
+ - "Cannot find required key 'name'. Path: ''"
+ - "Enum 'ab' does not exist. Path: '/blood'"
+ - "Key 'nam' was not defined. Path: ''"
+ - "Value 'foo(at)mail.com' does not match pattern '.+@.+'. Path: '/email'"
+ - "Value 'twenty' is not of type 'int'. Path: '/age'"
+ ## Kwalify errors
+ # :required_nokey : 1:1:[/] key 'name:' is required.
+ # :key_undefined : 1:1:[/nam] key 'nam:' is undefined.
+ # :pattern_unmatch : 2:1:[/email] 'foo(at)mail.com': not matched to pattern /@/.
+ # :type_unmatch : 3:1:[/age] 'twenty': not a integer.
+ # :enum_notexist : 4:1:[/blood] 'ab': invalid blood value.
+ # :type_unmatch : 5:1:[/birth] 'Jul 01, 1985': not a date.
+---
+name: fail-mapping-9
+desc: Test that regexes can be 'required'
+data:
+ hello: Hi
+ person: Fred
+schema:
+ type: map
+ mapping:
+ regex;(person[1-9]):
+ required: True
+errors:
+ - "Cannot find required key 'regex;(person[1-9])'. Path: ''"
+ - "Key 'hello' does not match any regex 'person[1-9]'. Path: ''"
+ - "Key 'person' does not match any regex 'person[1-9]'. Path: ''" \ No newline at end of file
diff --git a/tests/files/fail/test_matching.yaml b/tests/files/fail/test_matching.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_matching.yaml
diff --git a/tests/files/fail/test_merge.yaml b/tests/files/fail/test_merge.yaml
new file mode 100644
index 0000000..3b7eac8
--- /dev/null
+++ b/tests/files/fail/test_merge.yaml
@@ -0,0 +1,37 @@
+---
+name: fail-merge-1
+desc: merge maps
+schema:
+ type: map
+ mapping:
+ "group":
+ type: map
+ mapping:
+ "name": &name
+ type: str
+ required: true
+ "email": &email
+ type: str
+ pattern: .+@.+
+ required: False
+ "user":
+ type: map
+ mapping:
+ "name":
+ <<: *name # merge
+ length: {max: 16} # add
+ "email":
+ <<: *email # merge
+ required: true # override
+data:
+ group:
+ name: foo
+ email: foo@mail.com
+ user:
+ name: toooooo-looooong-naaaame
+errors:
+ - "Cannot find required key 'email'. Path: '/user'"
+ - "Value: 'toooooo-looooong-naaaame' has length of '24', greater than max limit '16'. Path: '/user/name'"
+ ## Kwalify errors
+ # :required_nokey : 5:3:[/user] key 'email:' is required.
+ # :length_toolong : 5:3:[/user/name] 'toooooo-looooong-naaaame': too long (length 24 > max 16).
diff --git a/tests/files/fail/test_name.yaml b/tests/files/fail/test_name.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_name.yaml
diff --git a/tests/files/fail/test_nullable.yaml b/tests/files/fail/test_nullable.yaml
new file mode 100644
index 0000000..8ce8259
--- /dev/null
+++ b/tests/files/fail/test_nullable.yaml
@@ -0,0 +1,19 @@
+---
+name: fail-nullable-1
+desc:
+data:
+ - name:
+ email: foo@mail.com
+ - email: bar@mail.net
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ name:
+ type: str
+ nullable: False
+ email:
+ type: str
+errors:
+ - "nullable.novalue : '/0/name'"
diff --git a/tests/files/fail/test_pattern.yaml b/tests/files/fail/test_pattern.yaml
new file mode 100644
index 0000000..4531d55
--- /dev/null
+++ b/tests/files/fail/test_pattern.yaml
@@ -0,0 +1,26 @@
+---
+name: fail-pattern-1
+desc:
+data:
+ email: foo(at)mail.com
+schema:
+ type: map
+ mapping:
+ email:
+ type: str
+ pattern: .+@.+
+errors:
+ - "Value 'foo(at)mail.com' does not match pattern '.+@.+'. Path: '/email'"
+---
+name: fail-pattern-2
+desc:
+data:
+ d: 'a'
+schema:
+ type: map
+ mapping:
+ d:
+ type: str
+ pattern: '[0-9]+'
+errors:
+ - "Value 'a' does not match pattern '[0-9]+'. Path: '/d'"
diff --git a/tests/files/fail/test_range.yaml b/tests/files/fail/test_range.yaml
new file mode 100644
index 0000000..22bd801
--- /dev/null
+++ b/tests/files/fail/test_range.yaml
@@ -0,0 +1,219 @@
+---
+name: fail-range-1
+desc:
+data:
+ - foo
+ - bar
+ - foobar
+schema:
+ type: seq
+ sequence:
+ - type: str
+ range:
+ max: 5
+ min: 1
+errors:
+ - "Type 'scalar' has size of '6', greater than max limit '5'. Path: '/2'"
+---
+name: fail-range-2
+desc: Test that range validates on 'map' raise correct error
+data:
+ streams:
+ sampleRateMultiple: 1
+schema:
+ type: map
+ mapping:
+ streams:
+ type: map
+ range:
+ min: 2
+ max: 3
+ mapping:
+ sampleRateMultiple:
+ type: int
+ required: True
+errors:
+ - "Type 'map' has size of '1', less than min limit '2'. Path: '/streams'"
+---
+name: fail-range-3
+desc: Test that range validates on 'seq' raise correct error
+data:
+ - foobar
+ - barfoo
+ - opa
+schema:
+ type: seq
+ range:
+ min: 1
+ max: 2
+ sequence:
+ - type: str
+errors:
+ - "Type 'seq' has size of '3', greater than max limit '2'. Path: ''"
+---
+name: fail-range-4
+desc: Test float range value out of range
+data:
+ the_float: 1.2
+ the_float_ex: 2.1
+schema:
+ type: map
+ mapping:
+ the_float:
+ type: float
+ required: True
+ range:
+ min: 2.1
+ max: 3.2
+ the_float_ex:
+ type: float
+ required: True
+ range:
+ min-ex: 2.1
+ max-ex: 3.2
+errors:
+ - "Type 'scalar' has size of '1.2', less than min limit '2.1'. Path: '/the_float'"
+ - "Type 'scalar' has size of '2.1', less than or equals to min limit(exclusive) '2.1'. Path: '/the_float_ex'"
+---
+name: fail-range-1
+desc: range test && bug#?????
+schema:
+ type: map
+ mapping:
+ "max-only":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {max: 100}
+ "min-only":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {min: 10.0}
+ "max-and-min":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {max: 100.0, min: 10.0}
+data:
+ max-only:
+ - 101
+ - 100.1
+ min-only:
+ - 9
+ - 9.99
+ max-and-min:
+ - 101
+ - 100.1
+ - 9
+ - 9.99
+errors:
+ - "Type 'scalar' has size of '100.1', greater than max limit '100'. Path: '/max-only/1'"
+ - "Type 'scalar' has size of '100.1', greater than max limit '100.0'. Path: '/max-and-min/1'"
+ - "Type 'scalar' has size of '101', greater than max limit '100'. Path: '/max-only/0'"
+ - "Type 'scalar' has size of '101', greater than max limit '100.0'. Path: '/max-and-min/0'"
+ - "Type 'scalar' has size of '9', less than min limit '10.0'. Path: '/max-and-min/2'"
+ - "Type 'scalar' has size of '9', less than min limit '10.0'. Path: '/min-only/0'"
+ - "Type 'scalar' has size of '9.99', less than min limit '10.0'. Path: '/max-and-min/3'"
+ - "Type 'scalar' has size of '9.99', less than min limit '10.0'. Path: '/min-only/1'"
+ ## Kwalify errors
+ # :range_toolarge : 2:3:[/max-only/0] '101': too large (> max 100).
+ # :range_toolarge : 3:3:[/max-only/1] '100.1': too large (> max 100).
+ # :range_toosmall : 5:3:[/min-only/0] '9': too small (< min 10.0).
+ # :range_toosmall : 6:3:[/min-only/1] '9.99': too small (< min 10.0).
+ # :range_toolarge : 8:3:[/max-and-min/0] '101': too large (> max 100.0).
+ # :range_toolarge : 9:3:[/max-and-min/1] '100.1': too large (> max 100.0).
+ # :range_toosmall : 10:3:[/max-and-min/2] '9': too small (< min 10.0).
+ # :range_toosmall : 11:3:[/max-and-min/3] '9.99': too small (< min 10.0).
+---
+name: fail-range-2
+desc: range test (with max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "max-ex-only":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {max-ex: 100}
+ "min-ex-only":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {min-ex: 10.0}
+ "max-ex-and-min-ex":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {max-ex: 100.0, min-ex: 10.0}
+data:
+ max-ex-only:
+ - 100
+ - 100.0
+ min-ex-only:
+ - 10
+ - 10.0
+ max-ex-and-min-ex:
+ - 100
+ - 100.0
+ - 10
+ - 10.0
+errors:
+ - "Type 'scalar' has size of '10', less than or equals to min limit(exclusive) '10.0'. Path: '/max-ex-and-min-ex/2'"
+ - "Type 'scalar' has size of '10', less than or equals to min limit(exclusive) '10.0'. Path: '/min-ex-only/0'"
+ - "Type 'scalar' has size of '10.0', less than or equals to min limit(exclusive) '10.0'. Path: '/max-ex-and-min-ex/3'"
+ - "Type 'scalar' has size of '10.0', less than or equals to min limit(exclusive) '10.0'. Path: '/min-ex-only/1'"
+ - "Type 'scalar' has size of '100', greater than or equals to max limit(exclusive) '100'. Path: '/max-ex-only/0'"
+ - "Type 'scalar' has size of '100', greater than or equals to max limit(exclusive) '100.0'. Path: '/max-ex-and-min-ex/0'"
+ - "Type 'scalar' has size of '100.0', greater than or equals to max limit(exclusive) '100'. Path: '/max-ex-only/1'"
+ - "Type 'scalar' has size of '100.0', greater than or equals to max limit(exclusive) '100.0'. Path: '/max-ex-and-min-ex/1'"
+ ## Kwalify errors
+ # :range_toolargeex : 2:3:[/max-ex-only/0] '100': too large (>= max 100).
+ # :range_toolargeex : 3:3:[/max-ex-only/1] '100.0': too large (>= max 100).
+ # :range_toosmallex : 5:3:[/min-ex-only/0] '10': too small (<= min 10.0).
+ # :range_toosmallex : 6:3:[/min-ex-only/1] '10.0': too small (<= min 10.0).
+ # :range_toolargeex : 8:3:[/max-ex-and-min-ex/0] '100': too large (>= max 100.0).
+ # :range_toolargeex : 9:3:[/max-ex-and-min-ex/1] '100.0': too large (>= max 100.0).
+ # :range_toosmallex : 10:3:[/max-ex-and-min-ex/2] '10': too small (<= min 10.0).
+ # :range_toosmallex : 11:3:[/max-ex-and-min-ex/3] '10.0': too small (<= min 10.0).
+---
+name: fail-range-3
+desc: range test (with max, min, max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "A":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {max: 100, min-ex: 10.0}
+ "B":
+ type: seq
+ sequence:
+ - type: number
+ required: true
+ range: {min: 10, max-ex: 100.0}
+data:
+ A:
+ - 100.00001
+ - 10.0
+ B:
+ - 9.99999
+ - 100.0
+errors:
+ - "Type 'scalar' has size of '10.0', less than or equals to min limit(exclusive) '10.0'. Path: '/A/1'"
+ - "Type 'scalar' has size of '100.0', greater than or equals to max limit(exclusive) '100.0'. Path: '/B/1'"
+ - "Type 'scalar' has size of '100.00001', greater than max limit '100'. Path: '/A/0'"
+ - "Type 'scalar' has size of '9.99999', less than min limit '10'. Path: '/B/0'"
+ ## Kwalify errors
+ # :range_toolarge : 2:3:[/A/0] '100.00001': too large (> max 100)"
+ # :range_toosmallex : 3:3:[/A/1] '10.0': too small (<= min 10.0).
+ # :range_toosmall : 5:3:[/B/0] '9.99999': too small (< min 10).
+ # :range_toolargeex : 6:3:[/B/1] '100.0': too large (>= max 100.0).
diff --git a/tests/files/fail/test_required.yaml b/tests/files/fail/test_required.yaml
new file mode 100644
index 0000000..6038544
--- /dev/null
+++ b/tests/files/fail/test_required.yaml
@@ -0,0 +1,19 @@
+---
+name: fail-required-1
+desc:
+data:
+ - name: foo
+ email: foo@mail.com
+ - email: bar@mail.net
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ name:
+ type: str
+ required: True
+ email:
+ type: str
+errors:
+ - "Cannot find required key 'name'. Path: '/1'"
diff --git a/tests/files/fail/test_schema.yaml b/tests/files/fail/test_schema.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_schema.yaml
diff --git a/tests/files/fail/test_sequence.yaml b/tests/files/fail/test_sequence.yaml
new file mode 100644
index 0000000..e26c4a2
--- /dev/null
+++ b/tests/files/fail/test_sequence.yaml
@@ -0,0 +1,71 @@
+---
+name: fail-sequence-1
+desc:
+data:
+ - 1
+ - 2
+ - 3
+ - True
+ - False
+schema:
+ type: seq
+ sequence:
+ - type: str
+errors:
+ - "Value '1' is not of type 'str'. Path: '/0'"
+ - "Value '2' is not of type 'str'. Path: '/1'"
+ - "Value '3' is not of type 'str'. Path: '/2'"
+ - "Value 'True' is not of type 'str'. Path: '/3'"
+ - "Value 'False' is not of type 'str'. Path: '/4'"
+---
+name: fail-sequence-2
+desc:
+data:
+ - True
+ - False
+ - 1
+schema:
+ type: seq
+ sequence:
+ - type: bool
+errors:
+ - "Value '1' is not of type 'bool'. Path: '/2'"
+---
+name: fail-sequence-3
+desc: sequence test
+schema:
+ type: seq
+ required: true
+ sequence:
+ - type: str
+ required: true
+data:
+ - foo
+ - bar
+ -
+ - baz
+ - 100
+errors:
+ - "Value '100' is not of type 'str'. Path: '/4'"
+ - "required.novalue : '/2'"
+ ## Kwalify errors
+ # - "Value 'None' is not of type 'str'. Path: '/2'"
+ # :required_novalue : (line 3)[/2] value required but none.
+ # :type_unmatch : (line 5)[/4] '100': not a string.
+---
+name: fail-sequence-4
+desc: Test that very deep nested sequences fail when schema expected sequence but value was something else
+schema:
+ type: seq
+ sequence:
+ - type: seq
+ sequence:
+ - type: seq
+ sequence:
+ - type: seq
+ sequence:
+ - type: str
+data:
+ - - - 1
+errors:
+ - "Value '1' is not a list. Value path: '/0/0/0'"
diff --git a/tests/files/fail/test_sequence_multi.yaml b/tests/files/fail/test_sequence_multi.yaml
new file mode 100644
index 0000000..a5007a0
--- /dev/null
+++ b/tests/files/fail/test_sequence_multi.yaml
@@ -0,0 +1,31 @@
+---
+name: fail-sequence-multi-1
+desc: Test multiple sequence values with wrong sub type and 'all' matching rule
+data:
+ - "foo"
+schema:
+ type: seq
+ matching: "all"
+ seq:
+ - type: str
+ - type: int
+errors:
+ - "Value 'foo' is not of type 'int'. Path: '/0'"
+---
+name: fail-sequence-multi-2
+desc: Test multiple nested sequence values with error in level 2 with 'any' matching rule
+data:
+ - - 123
+ - "foobar"
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: str
+ - type: seq
+ matching: "any"
+ sequence:
+ - type: str
+errors:
+ - "Value '123' is not of type 'str'. Path: '/0/0'"
+ - "Value '[123]' is not of type 'str'. Path: '/0'"
diff --git a/tests/files/fail/test_type_any.yaml b/tests/files/fail/test_type_any.yaml
new file mode 100644
index 0000000..5c8e7fa
--- /dev/null
+++ b/tests/files/fail/test_type_any.yaml
@@ -0,0 +1 @@
+# Becuase type 'any' validates for any kind of data there is no failure case for this type.
diff --git a/tests/files/fail/test_type_bool.yaml b/tests/files/fail/test_type_bool.yaml
new file mode 100644
index 0000000..1007348
--- /dev/null
+++ b/tests/files/fail/test_type_bool.yaml
@@ -0,0 +1,52 @@
+---
+name: fail-type-bool-1
+desc: Test wrong type as value in list
+data:
+ - "foo"
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: bool
+errors:
+ - "Value 'foo' is not of type 'bool'. Path: '/0'"
+---
+name: fail-type-bool-2
+desc: Test bool value inside list
+data:
+ - 'abc'
+ - 123
+schema:
+ type: seq
+ sequence:
+ - type: bool
+errors:
+ - "Value '123' is not of type 'bool'. Path: '/1'"
+ - "Value 'abc' is not of type 'bool'. Path: '/0'"
+---
+name: fail-type-bool-3
+desc: Test bool value in mapping
+data:
+ foo: 'abc'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: bool
+errors:
+ - "Value 'abc' is not of type 'bool'. Path: '/foo'"
+---
+name: fail-type-bool-4
+desc: Test bool inside nested map & seq
+data:
+ foo:
+ - 'abc'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: bool
+errors:
+ - "Value 'abc' is not of type 'bool'. Path: '/foo/0'"
diff --git a/tests/files/fail/test_type_date.yaml b/tests/files/fail/test_type_date.yaml
new file mode 100644
index 0000000..5a1b39a
--- /dev/null
+++ b/tests/files/fail/test_type_date.yaml
@@ -0,0 +1,83 @@
+---
+name: fail-type-date-1
+desc: basic test for date type with default formats
+data: "abc"
+schema:
+ type: date
+errors:
+ - "Not a valid date: abc Path: ''"
+---
+name: fail-type-date-2
+desc: Basic test for date type with defined date-formats
+data: "31-01-2017"
+schema:
+ type: date
+ format: "%Y-%m-%d"
+errors:
+ - "Not a valid date: 31-01-2017 format: %Y-%m-%d. Path: ''"
+---
+name: fail-type-date-3
+desc: Basic test for date type with defined date-formats
+data:
+ - "2017"
+ - "31"
+schema:
+ type: seq
+ sequence:
+ - type: date
+ format:
+ - "%d-%m-%Y"
+ - "%Y-%m-%d"
+errors:
+ - "Not a valid date: 2017 format: %Y-%m-%d. Path: '/0'"
+ - "Not a valid date: 31 format: %Y-%m-%d. Path: '/1'"
+---
+name: fail-type-data-4
+desc: Test date type as values in a list
+data:
+ - 'abc-1997'
+ - 'abc-1997-07'
+ - 'abc-1997-07-16'
+ - 'abc-1997-07-16T19:20+01:00'
+ - 'abc-1997-07-16T19:20:30+01:00'
+ - 'abc-1997-07-16T19:20:30.45+01:00'
+schema:
+ type: seq
+ sequence:
+ - type: date
+errors:
+ - "Not a valid date: abc-1997 Path: '/0'"
+ - "Not a valid date: abc-1997-07 Path: '/1'"
+ - "Not a valid date: abc-1997-07-16 Path: '/2'"
+ - "Not a valid date: abc-1997-07-16T19:20+01:00 Path: '/3'"
+ - "Not a valid date: abc-1997-07-16T19:20:30+01:00 Path: '/4'"
+ - "Not a valid date: abc-1997-07-16T19:20:30.45+01:00 Path: '/5'"
+---
+name: fail-type-date-5
+desc: Test that wrong value types do not validate
+data:
+ - 123
+ - True
+schema:
+ type: seq
+ sequence:
+ - type: date
+errors:
+ - "Value '123' is not of type 'date'. Path: '/0'"
+ - "Value 'True' is not of type 'date'. Path: '/1'"
+---
+name: fail-type-date-6
+desc: Test that wrong value types in map do not validate
+data:
+ foo: 123
+ bar: True
+schema:
+ type: map
+ mapping:
+ foo:
+ type: date
+ bar:
+ type: date
+errors:
+ - "Value '123' is not of type 'date'. Path: '/foo'"
+ - "Value 'True' is not of type 'date'. Path: '/bar'"
diff --git a/tests/files/fail/test_type_float.yaml b/tests/files/fail/test_type_float.yaml
new file mode 100644
index 0000000..bef14b8
--- /dev/null
+++ b/tests/files/fail/test_type_float.yaml
@@ -0,0 +1,56 @@
+---
+name: fail-type-float-1
+desc: Test simples float value
+data: "abc"
+schema:
+ type: float
+errors:
+ - "Value 'abc' is not of type 'float'. Path: ''"
+---
+name: fail-type-float-2
+desc: Test wrong type as value in list
+data:
+ - "foo"
+ - True
+schema:
+ type: seq
+ seq:
+ - type: float
+errors:
+ - "Value 'foo' is not of type 'float'. Path: '/0'"
+errors:
+ - "Value 'True' is not of type 'float'. Path: '/1'"
+ - "Value 'foo' is not of type 'float'. Path: '/0'"
+---
+name: fail-type-float-3
+desc: Test float value in mapping
+data:
+ foo: "abc"
+ bar: True
+schema:
+ type: map
+ mapping:
+ foo:
+ type: float
+ bar:
+ type: float
+errors:
+ - "Value 'True' is not of type 'float'. Path: '/bar'"
+ - "Value 'abc' is not of type 'float'. Path: '/foo'"
+---
+name: fail-type-float-4
+desc: Test float inside nested map & seq
+data:
+ foo:
+ - True
+ - "abc"
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: float
+errors:
+ - "Value 'True' is not of type 'float'. Path: '/foo/0'"
+ - "Value 'abc' is not of type 'float'. Path: '/foo/1'"
diff --git a/tests/files/fail/test_type_int.yaml b/tests/files/fail/test_type_int.yaml
new file mode 100644
index 0000000..62d8fe1
--- /dev/null
+++ b/tests/files/fail/test_type_int.yaml
@@ -0,0 +1,45 @@
+---
+name: fail-type-int-1
+desc:
+data:
+ A101
+schema:
+ type: int
+errors:
+ - "Value 'A101' is not of type 'int'. Path: ''"
+---
+name: fail-type-int-2
+desc: Test wrong type as value in list
+data:
+ - "foo"
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: int
+errors:
+ - "Value 'foo' is not of type 'int'. Path: '/0'"
+---
+name: fail-type-int-3
+desc: Test that True/False is not valid integers
+data:
+ - 1
+ - True
+ - False
+schema:
+ type: seq
+ sequence:
+ - type: int
+errors:
+ - "Value 'False' is not of type 'int'. Path: '/2'"
+ - "Value 'True' is not of type 'int'. Path: '/1'"
+---
+name: fail-type-int-4
+desc: Test that hexadecimal characters fails with pattern
+data:
+ 0x12345678
+schema:
+ type: text
+ pattern: ^0x[0-9A-F]{1,8}$
+errors:
+ - "Value '305419896' does not match pattern '^0x[0-9A-F]{1,8}$'. Path: ''"
diff --git a/tests/files/fail/test_type_map.yaml b/tests/files/fail/test_type_map.yaml
new file mode 100644
index 0000000..a46086b
--- /dev/null
+++ b/tests/files/fail/test_type_map.yaml
@@ -0,0 +1,46 @@
+---
+name: fail-type-map-1
+desc: Test the most basic case for map
+data:
+ - 'foo'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+errors:
+ - "Value '['foo']' is not a dict. Value path: ''"
+---
+name: type-map-2
+desc:
+data:
+ - - 'foo'
+ - - 'foo'
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: str
+errors:
+ - "Value '['foo']' is not a dict. Value path: '/0'"
+ - "Value '['foo']' is not a dict. Value path: '/1'"
+---
+name: type-map-3
+desc: Test bool inside nested map & seq
+data:
+ foo:
+ - - 'foo'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ bar:
+ type: str
+errors:
+ - "Value '['foo']' is not a dict. Value path: '/foo/0'"
diff --git a/tests/files/fail/test_type_none.yaml b/tests/files/fail/test_type_none.yaml
new file mode 100644
index 0000000..cc7c7df
--- /dev/null
+++ b/tests/files/fail/test_type_none.yaml
@@ -0,0 +1,54 @@
+#
+# NOTE: This case is not allowed becuase Core class do NOT allow
+# there is no data to validate. This happens if None is at top level
+# of the data structure.
+#
+# ---
+# name: type-none-1
+# desc: Most basic test for type None
+# data: ~
+# schema:
+# type: none
+---
+name: fail-type-none-2
+desc: Test that none type works with none type as value in map
+data:
+ name: 'abc'
+schema:
+ type: map
+ mapping:
+ name:
+ type: none
+errors:
+ - "Value 'abc' is not of type 'none'. Path: '/name'"
+---
+name: fail-type-none-3
+desc: Test that none type works as value in sequence
+data:
+ - 'abc'
+ - 123
+schema:
+ type: seq
+ sequence:
+ - type: none
+errors:
+ - "Value '123' is not of type 'none'. Path: '/1'"
+ - "Value 'abc' is not of type 'none'. Path: '/0'"
+---
+name: fail-type-none-4
+desc: Test that none type works inside nested map, seq, map
+data:
+ foo:
+ - bar: 'abc'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ bar:
+ type: none
+errors:
+ - "Value 'abc' is not of type 'none'. Path: '/foo/0/bar'"
diff --git a/tests/files/fail/test_type_number.yaml b/tests/files/fail/test_type_number.yaml
new file mode 100644
index 0000000..003b0a6
--- /dev/null
+++ b/tests/files/fail/test_type_number.yaml
@@ -0,0 +1,83 @@
+---
+name: fail-type-number-1
+desc: This tests number validation rule with wrong data types
+data: True
+schema:
+ type: number
+errors:
+ - "Value 'True' is not of type 'number'. Path: ''"
+---
+name: fail-type-number-2
+desc: Test that number type works with as value in map
+data:
+ foo: True
+ bar: 'abc'
+ qwe: []
+ rty: {}
+schema:
+ type: map
+ mapping:
+ foo:
+ type: number
+ bar:
+ type: number
+ qwe:
+ type: number
+ rty:
+ type: number
+errors:
+ - "Value 'True' is not of type 'number'. Path: '/foo'"
+ - "Value '[]' is not of type 'number'. Path: '/qwe'"
+ - "Value 'abc' is not of type 'number'. Path: '/bar'"
+ - "Value '{}' is not of type 'number'. Path: '/rty'"
+---
+name: fail-type-number-3
+desc: Test that different number values works as values in seq
+data:
+ - True
+ - 'abc'
+ - {}
+ - []
+schema:
+ type: seq
+ sequence:
+ - type: number
+errors:
+ - "Value 'True' is not of type 'number'. Path: '/0'"
+ - "Value '[]' is not of type 'number'. Path: '/3'"
+ - "Value 'abc' is not of type 'number'. Path: '/1'"
+ - "Value '{}' is not of type 'number'. Path: '/2'"
+---
+name: fail-type-number-4
+desc: Test that number type works inside nested map, seq, map
+data:
+ foobar:
+ - foo: True
+ bar: 'abc'
+ qwe: {}
+ rty: []
+schema:
+ type: map
+ mapping:
+ foobar:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: number
+ bar:
+ type: number
+ qwe:
+ type: number
+ rty:
+ type: number
+ ewq:
+ type: number
+ dsa:
+ type: number
+errors:
+ - "Value 'True' is not of type 'number'. Path: '/foobar/0/foo'"
+ - "Value '[]' is not of type 'number'. Path: '/foobar/0/rty'"
+ - "Value 'abc' is not of type 'number'. Path: '/foobar/0/bar'"
+ - "Value '{}' is not of type 'number'. Path: '/foobar/0/qwe'"
diff --git a/tests/files/fail/test_type_scalar.yaml b/tests/files/fail/test_type_scalar.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_type_scalar.yaml
diff --git a/tests/files/fail/test_type_seq.yaml b/tests/files/fail/test_type_seq.yaml
new file mode 100644
index 0000000..a5baacd
--- /dev/null
+++ b/tests/files/fail/test_type_seq.yaml
@@ -0,0 +1,47 @@
+#
+# TODO: All of these failure tests currently raises a strange error that might not be the correct one and might require some redesign of the implementation.
+# pykwalify.errors.NotSequenceError: <NotSequenceError: error code 7: Value: {} is not of a sequence type: Path: '/'>
+#
+
+# ---
+# name: fail-type-seq-1
+# desc: Test the most basic case for seq
+# data:
+# {}
+# schema:
+# type: seq
+# sequence:
+# - type: str
+# errors:
+# - ''
+# ---
+# name: fail-type-seq-2
+# desc: Test that seq in seq works
+# data:
+# - {}
+# - {}
+# schema:
+# type: seq
+# sequence:
+# - type: seq
+# sequence:
+# - type: bool
+# errors:
+# - ''
+# ---
+# name: fail-type-seq-3
+# desc: Test bool inside nested map & seq
+# data:
+# - foo:
+# {}
+# schema:
+# type: seq
+# sequence:
+# - type: map
+# mapping:
+# foo:
+# type: seq
+# sequence:
+# - type: bool
+# errors:
+# - ''
diff --git a/tests/files/fail/test_type_str.yaml b/tests/files/fail/test_type_str.yaml
new file mode 100644
index 0000000..13cdbb6
--- /dev/null
+++ b/tests/files/fail/test_type_str.yaml
@@ -0,0 +1,73 @@
+---
+name: fail-type-str-1
+desc: Test simples str value
+data: 1
+schema:
+ type: str
+errors:
+ - "Value '1' is not of type 'str'. Path: ''"
+---
+name: fail-type-str-2
+desc: Test str value inside list
+data:
+ - 1
+ - True
+schema:
+ type: seq
+ sequence:
+ - type: str
+errors:
+ - "Value '1' is not of type 'str'. Path: '/0'"
+ - "Value 'True' is not of type 'str'. Path: '/1'"
+---
+name: fail-type-str-3
+desc: Test str value in mapping
+data:
+ foo: 1
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+errors:
+ - "Value '1' is not of type 'str'. Path: '/foo'"
+---
+name: fail-type-str-4
+desc: Test str inside nested map & seq
+data:
+ foo:
+ - 1
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: str
+errors:
+ - "Value '1' is not of type 'str'. Path: '/foo/0'"
+---
+name: fail-deftype-1
+desc: default type test
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ "email":
+data:
+ - name: 123
+ email: true
+ - name: 3.14
+ - email: 2004-01-01
+errors:
+ - "Value '123' is not of type 'str'. Path: '/0/name'"
+ - "Value '2004-01-01' is not of type 'str'. Path: '/2/email'"
+ - "Value '3.14' is not of type 'str'. Path: '/1/name'"
+ - "Value 'True' is not of type 'str'. Path: '/0/email'"
+ ## Kwalify errors
+ # :type_unmatch : 1:3:[/0/name] '123': not a string.
+ # :type_unmatch : 2:3:[/0/email] 'true': not a string.
+ # :type_unmatch : 3:3:[/1/name] '3.14': not a string.
+ # :type_unmatch : 4:3:[/2/email] '2004-01-01': not a string.
diff --git a/tests/files/fail/test_type_symbol.yaml b/tests/files/fail/test_type_symbol.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_type_symbol.yaml
diff --git a/tests/files/fail/test_type_text.yaml b/tests/files/fail/test_type_text.yaml
new file mode 100644
index 0000000..f4e6d2e
--- /dev/null
+++ b/tests/files/fail/test_type_text.yaml
@@ -0,0 +1,70 @@
+---
+name: fail-type-text-1
+desc: Test simples text type
+data: True
+schema:
+ type: text
+errors:
+ - "Value 'True' is not of type 'text'. Path: ''"
+---
+name: fail-type-text-2
+desc: Test possible values as values in seq
+data:
+ - abc
+ - 123
+ - 3.14159
+ - True
+schema:
+ type: seq
+ sequence:
+ - type: text
+errors:
+ - "Value 'True' is not of type 'text'. Path: '/3'"
+---
+name: fail-type-text-3
+desc: Test possible values as values in map
+data:
+ foo: abc
+ bar: 123
+ qwe: 3.14159
+ rty: True
+schema:
+ type: map
+ mapping:
+ foo:
+ type: text
+ bar:
+ type: text
+ qwe:
+ type: text
+ rty:
+ type: text
+errors:
+ - "Value 'True' is not of type 'text'. Path: '/rty'"
+---
+name: fail-type-text-4
+desc: Test that text type works inside nested map, seq, map
+data:
+ foobar:
+ - foo: abc
+ bar: 123
+ qwe: 3.14159
+ rty: True
+schema:
+ type: map
+ mapping:
+ foobar:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: text
+ bar:
+ type: text
+ qwe:
+ type: text
+ rty:
+ type: text
+errors:
+ - "Value 'True' is not of type 'text'. Path: '/foobar/0/rty'"
diff --git a/tests/files/fail/test_type_timestamp.yaml b/tests/files/fail/test_type_timestamp.yaml
new file mode 100644
index 0000000..1a5e120
--- /dev/null
+++ b/tests/files/fail/test_type_timestamp.yaml
@@ -0,0 +1,15 @@
+---
+name: fail-type-timestamp-1
+desc: Test timestamps that should throw errors
+data:
+ d1: ""
+ d2: "1427650980"
+schema:
+ type: map
+ mapping:
+ d1:
+ type: timestamp
+ d2:
+ type: timestamp
+errors:
+ - "Timestamp value is empty. Path: '/d1'"
diff --git a/tests/files/fail/test_unique.yaml b/tests/files/fail/test_unique.yaml
new file mode 100644
index 0000000..0af5d7c
--- /dev/null
+++ b/tests/files/fail/test_unique.yaml
@@ -0,0 +1,109 @@
+---
+name: fail-unique-1
+desc: "NOTE: The reverse unique do not currently work proper # This will test the unique constraint but should fail"
+data:
+ - name: foo
+ email: admin@mail.com
+ groups:
+ - foo
+ - users
+ - admin
+ - foo
+ - name: bar
+ email: admin@mail.com
+ groups:
+ - admin
+ - users
+ - name: bar
+ email: baz@mail.com
+ groups:
+ - users
+schema:
+ type: seq
+ sequence:
+ - type: map
+ required: True
+ mapping:
+ name:
+ type: str
+ required: True
+ unique: True
+ email:
+ type: str
+ groups:
+ type: seq
+ sequence:
+ - type: str
+ unique: True
+errors:
+ - "Value 'bar' is not unique. Previous path: '/1/name'. Path: '/2/name'"
+ - "Value 'foo' is not unique. Previous path: '/0/groups/0'. Path: '/0/groups/3'"
+---
+name: fail-unique-2
+desc: unique constraint test with map
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ unique: true
+ "age":
+ type: int
+data:
+ - name: foo
+ age: 10
+ - name: bar
+ age: 10
+ - age: 10
+ name: bar
+errors:
+ - "Value 'bar' is not unique. Previous path: '/1/name'. Path: '/2/name'"
+ ## Kwalify errors
+ # :value_notunique : 6:3:[/2/name] 'bar': is already used at '/1/name'.
+---
+name: fail-unique-3
+desc: unique constraint test with seq
+schema:
+ type: seq
+ sequence:
+ - type: str
+ unique: true
+data:
+ - foo
+ - ~
+ - bar
+ - ~
+ - bar
+errors:
+ - "Value 'bar' is not unique. Previous path: '/2'. Path: '/4'"
+ ## Kwalify errors
+ # :value_notunique : 5:1:[/4] 'bar': is already used at '/2'.
+---
+name: fail-unique-4
+desc: unique constraint and '<<' (merge)
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ type: str
+ required: true
+ unique: true
+ "value":
+ type: any
+ required: true
+data:
+ - &a1
+ name: x1
+ value: 10
+ - <<: *a1
+ - <<: *a1 # wrong validation error
+ name: x3
+errors:
+ - "Value 'x1' is not unique. Previous path: '/0/name'. Path: '/1/name'"
+ # TODO: Possibly missing one error here...
+ ## Kwalify errors
+ # :value_notunique : 4:3:[/1/name] 'x1': is already used at '/0/name'.
+ # :value_notunique : 5:3:[/2/name] 'x1': is already used at '/0/name'.
diff --git a/tests/files/fail/test_version.yaml b/tests/files/fail/test_version.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/fail/test_version.yaml
diff --git a/tests/files/partial_schemas/1f-data.yaml b/tests/files/partial_schemas/1f-data.yaml
new file mode 100644
index 0000000..4444bad
--- /dev/null
+++ b/tests/files/partial_schemas/1f-data.yaml
@@ -0,0 +1 @@
+- foo: "opa" \ No newline at end of file
diff --git a/tests/files/partial_schemas/1f-partials.yaml b/tests/files/partial_schemas/1f-partials.yaml
new file mode 100644
index 0000000..b93127a
--- /dev/null
+++ b/tests/files/partial_schemas/1f-partials.yaml
@@ -0,0 +1,11 @@
+schema;fooone:
+ type: map
+ mapping:
+ foo:
+ include: footwo
+
+schema;footwo:
+ type: map
+ mapping:
+ foo:
+ type: bool
diff --git a/tests/files/partial_schemas/1f-schema.yaml b/tests/files/partial_schemas/1f-schema.yaml
new file mode 100644
index 0000000..462d391
--- /dev/null
+++ b/tests/files/partial_schemas/1f-schema.yaml
@@ -0,0 +1,3 @@
+type: seq
+sequence:
+ - include: fooonez
diff --git a/tests/files/partial_schemas/1s-data.yaml b/tests/files/partial_schemas/1s-data.yaml
new file mode 100644
index 0000000..4444bad
--- /dev/null
+++ b/tests/files/partial_schemas/1s-data.yaml
@@ -0,0 +1 @@
+- foo: "opa" \ No newline at end of file
diff --git a/tests/files/partial_schemas/1s-partials.yaml b/tests/files/partial_schemas/1s-partials.yaml
new file mode 100644
index 0000000..ef84657
--- /dev/null
+++ b/tests/files/partial_schemas/1s-partials.yaml
@@ -0,0 +1,11 @@
+schema;fooone:
+ type: map
+ mapping:
+ foo:
+ type: str
+
+schema;footwo:
+ type: map
+ mapping:
+ foo:
+ type: bool \ No newline at end of file
diff --git a/tests/files/partial_schemas/1s-schema.yaml b/tests/files/partial_schemas/1s-schema.yaml
new file mode 100644
index 0000000..ea433b7
--- /dev/null
+++ b/tests/files/partial_schemas/1s-schema.yaml
@@ -0,0 +1,3 @@
+type: seq
+sequence:
+ - include: fooone
diff --git a/tests/files/partial_schemas/2f-data.yaml b/tests/files/partial_schemas/2f-data.yaml
new file mode 100644
index 0000000..b1d5359
--- /dev/null
+++ b/tests/files/partial_schemas/2f-data.yaml
@@ -0,0 +1 @@
+- True
diff --git a/tests/files/partial_schemas/2f-schema.yaml b/tests/files/partial_schemas/2f-schema.yaml
new file mode 100644
index 0000000..d5bd186
--- /dev/null
+++ b/tests/files/partial_schemas/2f-schema.yaml
@@ -0,0 +1,5 @@
+type: seq
+sequence:
+ - include: foo
+schema;foo:
+ type: str
diff --git a/tests/files/partial_schemas/2s-data.yaml b/tests/files/partial_schemas/2s-data.yaml
new file mode 100644
index 0000000..c67a590
--- /dev/null
+++ b/tests/files/partial_schemas/2s-data.yaml
@@ -0,0 +1,3 @@
+- foo:
+ bar:
+ - true
diff --git a/tests/files/partial_schemas/2s-partials.yaml b/tests/files/partial_schemas/2s-partials.yaml
new file mode 100644
index 0000000..238abb9
--- /dev/null
+++ b/tests/files/partial_schemas/2s-partials.yaml
@@ -0,0 +1,16 @@
+schema;footwo:
+ type: map
+ mapping:
+ bar:
+ include: foothree
+
+schema;fooone:
+ type: map
+ mapping:
+ foo:
+ include: footwo
+
+schema;foothree:
+ type: seq
+ sequence:
+ - type: bool
diff --git a/tests/files/partial_schemas/2s-schema.yaml b/tests/files/partial_schemas/2s-schema.yaml
new file mode 100644
index 0000000..ea433b7
--- /dev/null
+++ b/tests/files/partial_schemas/2s-schema.yaml
@@ -0,0 +1,3 @@
+type: seq
+sequence:
+ - include: fooone
diff --git a/tests/files/partial_schemas/3f-data.yaml b/tests/files/partial_schemas/3f-data.yaml
new file mode 100644
index 0000000..0ca9514
--- /dev/null
+++ b/tests/files/partial_schemas/3f-data.yaml
@@ -0,0 +1 @@
+True
diff --git a/tests/files/partial_schemas/3f-schema.yaml b/tests/files/partial_schemas/3f-schema.yaml
new file mode 100644
index 0000000..d83c47c
--- /dev/null
+++ b/tests/files/partial_schemas/3f-schema.yaml
@@ -0,0 +1,3 @@
+include: foo
+schema;foo:
+ type: str
diff --git a/tests/files/partial_schemas/4f-data.yaml b/tests/files/partial_schemas/4f-data.yaml
new file mode 100644
index 0000000..54c108e
--- /dev/null
+++ b/tests/files/partial_schemas/4f-data.yaml
@@ -0,0 +1,2 @@
+- foo:
+ - bar: True
diff --git a/tests/files/partial_schemas/4f-schema.yaml b/tests/files/partial_schemas/4f-schema.yaml
new file mode 100644
index 0000000..d109e40
--- /dev/null
+++ b/tests/files/partial_schemas/4f-schema.yaml
@@ -0,0 +1,20 @@
+type: seq
+sequence:
+ - include: fooone
+
+schema;fooone:
+ type: map
+ mapping:
+ foo:
+ include: footwo
+
+schema;footwo:
+ type: seq
+ sequence:
+ - include: foothree
+
+schema;foothree:
+ type: map
+ mapping:
+ bar:
+ type: str
diff --git a/tests/files/partial_schemas/5f-data.yaml b/tests/files/partial_schemas/5f-data.yaml
new file mode 100644
index 0000000..78cc18b
--- /dev/null
+++ b/tests/files/partial_schemas/5f-data.yaml
@@ -0,0 +1 @@
+- - - - True
diff --git a/tests/files/partial_schemas/5f-schema.yaml b/tests/files/partial_schemas/5f-schema.yaml
new file mode 100644
index 0000000..788d906
--- /dev/null
+++ b/tests/files/partial_schemas/5f-schema.yaml
@@ -0,0 +1,18 @@
+type: seq
+sequence:
+ - include: fooone
+
+schema;fooone:
+ type: seq
+ sequence:
+ - include: footwo
+
+schema;footwo:
+ type: seq
+ sequence:
+ - include: foothree
+
+schema;foothree:
+ type: seq
+ sequence:
+ - type: str
diff --git a/tests/files/partial_schemas/6f-data.yaml b/tests/files/partial_schemas/6f-data.yaml
new file mode 100644
index 0000000..49b7ed7
--- /dev/null
+++ b/tests/files/partial_schemas/6f-data.yaml
@@ -0,0 +1,4 @@
+foo:
+ bar:
+ qwe:
+ ewq: True
diff --git a/tests/files/partial_schemas/6f-schema.yaml b/tests/files/partial_schemas/6f-schema.yaml
new file mode 100644
index 0000000..00c0a91
--- /dev/null
+++ b/tests/files/partial_schemas/6f-schema.yaml
@@ -0,0 +1,22 @@
+type: map
+mapping:
+ foo:
+ include: fooone
+
+schema;fooone:
+ type: map
+ mapping:
+ bar:
+ include: footwo
+
+schema;footwo:
+ type: map
+ mapping:
+ qwe:
+ include: foothree
+
+schema;foothree:
+ type: map
+ mapping:
+ ewq:
+ type: str
diff --git a/tests/files/partial_schemas/7s-data.yaml b/tests/files/partial_schemas/7s-data.yaml
new file mode 100644
index 0000000..d48b5a2
--- /dev/null
+++ b/tests/files/partial_schemas/7s-data.yaml
@@ -0,0 +1,5 @@
+foo: blah
+bar:
+ - blah
+ - blah
+ - blah
diff --git a/tests/files/partial_schemas/7s-schema.yaml b/tests/files/partial_schemas/7s-schema.yaml
new file mode 100644
index 0000000..eaf0613
--- /dev/null
+++ b/tests/files/partial_schemas/7s-schema.yaml
@@ -0,0 +1,12 @@
+type: map
+mapping:
+ foo:
+ type: str
+ required: True
+ bar:
+ include: bar
+schema;bar:
+ type: seq
+ required: True
+ sequence:
+ - type: str
diff --git a/tests/files/success/test_anchor.yaml b/tests/files/success/test_anchor.yaml
new file mode 100644
index 0000000..fa53412
--- /dev/null
+++ b/tests/files/success/test_anchor.yaml
@@ -0,0 +1,92 @@
+##
+---
+name: anchor1
+desc: schema with anchor
+#
+schema:
+ type: seq
+ required: true
+ sequence:
+ - type: map
+ required: true
+ mapping:
+ first-name: &name
+ type: str
+ required: True
+ family-name: *name
+#
+data:
+ - first-name: foo
+ family-name: Foo
+ - first-name: bar
+ family-name: Bar
+##
+---
+name: anchor2
+desc: schema with anchor 2
+#
+schema:
+ type: map
+ required: true
+ mapping:
+ title: &name
+ type: str
+ required: true
+ address-book:
+ type: seq
+ required: true
+ sequence:
+ - type: map
+ mapping:
+ name: *name
+ email:
+ type: str
+ required: True
+#
+data:
+ title: my friends
+ address-book:
+ - name: foo
+ email: foo@mail.com
+ - name: bar
+ email: bar@mail.com
+#
+# TODO: THIS TEST IS BROKEN BECUASE IT CAUSE INFINITE RECURSION IN PYTHON
+#
+# ##
+# ---
+# name: anchor3
+# desc: document with anchor
+# #
+# schema:
+# type: seq
+# sequence:
+# - &employee
+# type: map
+# mapping:
+# name:
+# type: str
+# post:
+# type: str
+# enum:
+# - exective
+# - manager
+# - clerk
+# supervisor: *employee
+# #
+# data:
+# - &foo
+# name: foo
+# post: exective
+# - &bar
+# name: bar
+# post: manager
+# supervisor: *foo
+# - &baz
+# name: baz
+# post: clerk
+# supervisor: *bar
+# - &zak
+# name: zak
+# post: clerk
+# supervisor: *bar
diff --git a/tests/files/success/test_assert.yaml b/tests/files/success/test_assert.yaml
new file mode 100644
index 0000000..46b3ef3
--- /dev/null
+++ b/tests/files/success/test_assert.yaml
@@ -0,0 +1,28 @@
+##
+---
+name: assert1
+desc: assert test
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "less-than":
+ type: number
+ assert: val < 8
+ "more-than":
+ type: number
+ assert: 3 < val
+ "between":
+ type: number
+ assert: 3 < val and val < 8
+ "except":
+ type: number
+ assert: val < 3 or 8 < val
+#
+data:
+ - less-than: 5
+ - more-than: 5
+ - between: 5
+ - except: 0
diff --git a/tests/files/success/test_default.yaml b/tests/files/success/test_default.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_default.yaml
diff --git a/tests/files/success/test_desc.yaml b/tests/files/success/test_desc.yaml
new file mode 100644
index 0000000..347a729
--- /dev/null
+++ b/tests/files/success/test_desc.yaml
@@ -0,0 +1,7 @@
+---
+name: desc-1
+desc: Test basic desc
+data: 'foobar'
+schema:
+ desc: This is a description...
+ type: str
diff --git a/tests/files/success/test_enum.yaml b/tests/files/success/test_enum.yaml
new file mode 100644
index 0000000..c583d42
--- /dev/null
+++ b/tests/files/success/test_enum.yaml
@@ -0,0 +1,12 @@
+---
+name: enum-1
+desc: Test simple enum
+data:
+ - A
+ - B
+ - O
+schema:
+ type: seq
+ sequence:
+ - type: str
+ enum: [A, B, O, AB]
diff --git a/tests/files/success/test_example.yaml b/tests/files/success/test_example.yaml
new file mode 100644
index 0000000..ee6965f
--- /dev/null
+++ b/tests/files/success/test_example.yaml
@@ -0,0 +1,5 @@
+---
+data: foo
+schema:
+ example: Foobar
+ type: str
diff --git a/tests/files/success/test_extensions.yaml b/tests/files/success/test_extensions.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_extensions.yaml
diff --git a/tests/files/success/test_func.yaml b/tests/files/success/test_func.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_func.yaml
diff --git a/tests/files/success/test_ident.yaml b/tests/files/success/test_ident.yaml
new file mode 100644
index 0000000..0f3c95c
--- /dev/null
+++ b/tests/files/success/test_ident.yaml
@@ -0,0 +1,22 @@
+##
+---
+name: ident1
+desc: ident constraint test
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ ident: true
+ "age":
+ type: int
+#
+data:
+ - name: foo
+ age: 10
+ - name: bar
+ age: 10
+ - name: baz
+ age: 10
diff --git a/tests/files/success/test_include.yaml b/tests/files/success/test_include.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_include.yaml
diff --git a/tests/files/success/test_length.yaml b/tests/files/success/test_length.yaml
new file mode 100644
index 0000000..1b8d7d8
--- /dev/null
+++ b/tests/files/success/test_length.yaml
@@ -0,0 +1,98 @@
+---
+name: length1
+desc: length test
+schema:
+ type: map
+ mapping:
+ "max-only":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max: 8
+ "min-only":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ min: 4
+ "max-and-min":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max: 8
+ min: 4
+data:
+ max-only:
+ - hogehoge
+ - a
+ -
+ min-only:
+ - hoge
+ - hogehogehogehogehoge
+ max-and-min:
+ - hogehoge
+ - hoge
+---
+name: length2
+desc: length test (with max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "max-ex-only":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max-ex: 8
+ "min-ex-only":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ min-ex: 4
+ "max-ex-and-min-ex":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max-ex: 8
+ min-ex: 4
+data:
+ max-ex-only:
+ - hogehog
+ - a
+ -
+ min-ex-only:
+ - hoge!
+ max-ex-and-min-ex:
+ - hogehog
+ - hoge!
+---
+name: length3
+desc: length test (with min, max, max-ex and min-ex)
+schema:
+ type: map
+ mapping:
+ "A":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max: 8
+ min-ex: 4
+ "B":
+ type: seq
+ sequence:
+ - type: str
+ length:
+ max-ex: 8
+ min: 4
+data:
+ A:
+ - hogehoge
+ - hogeh
+ B:
+ - hogehog
+ - hoge
diff --git a/tests/files/success/test_mapping.yaml b/tests/files/success/test_mapping.yaml
new file mode 100644
index 0000000..fb82fd6
--- /dev/null
+++ b/tests/files/success/test_mapping.yaml
@@ -0,0 +1,308 @@
+---
+name: mapping1
+desc: Most basic mapping validation
+data:
+ foo: bar
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+---
+name: mapping2
+desc: Complex mapping that test several subtypes for each key
+#
+schema:
+ type: map
+ required: true
+ mapping:
+ name:
+ type: str
+ required: true
+ email:
+ type: str
+ # This pattern value was modified from /@/ to .+@.+ to make it copmatible with python
+ pattern: .+@.+
+ required: True
+ age:
+ type: int
+ blood:
+ type: str
+ enum:
+ - A
+ - B
+ - O
+ - AB
+ birth:
+ type: date
+data:
+ name: foo
+ email: foo@mail.com
+ age: 20
+ blood: AB
+ birth: 1985-01-01
+---
+name: mapping3
+desc: Test that mapping works inside a sequence
+data:
+ - foo: True
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: bool
+---
+name: mapping4
+desc: Test that map inside seq inside map works
+data:
+ company: Kuwata lab.
+ email: webmaster@kuwata-lab.com
+ employees:
+ - code: 101
+ name: foo
+ email: foo@kuwata-lab.com
+ - code: 102
+ name: bar
+ email: bar@kuwata-lab.com
+schema:
+ type: map
+ mapping:
+ company:
+ type: str
+ required: True
+ email:
+ type: str
+ employees:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ code:
+ type: int
+ required: True
+ name:
+ type: str
+ required: True
+ email:
+ type: str
+---
+name: mapping5
+desc: test allowempty option to mapping
+data:
+ datasources:
+ test1: test1.py
+ test2: test2.py
+schema:
+ type: map
+ mapping:
+ datasources:
+ type: map
+ allowempty: True
+ mapping:
+ test1:
+ type: str
+---
+name: mapping6
+desc: Test that regex keys works
+data:
+ mic:
+ - input
+ - foo
+ mock:
+ - True
+ - False
+schema:
+ type: map
+ matching-rule: "any"
+ mapping:
+ re;(mi.+):
+ type: seq
+ sequence:
+ - type: str
+ regex;(mo.+):
+ type: seq
+ sequence:
+ - type: bool
+---
+name: mapping7
+desc: Test that mapping name works
+data:
+ datasources: test1.py
+schema:
+ type: map
+ mapping:
+ datasources:
+ type: str
+---
+name: mapping8
+desc: Test that map shortcut works
+data:
+ datasources: test1.py
+schema:
+ type: map
+ map:
+ datasources:
+ type: str
+---
+name: mapping9
+desc: Test that you do not have to specify type map
+data:
+ streams: foobar
+schema:
+ mapping:
+ streams:
+ type: str
+---
+name: mapping10
+desc: Test that you do not have to specify type map when map exists in schema
+data:
+ streams: foobar
+schema:
+ map:
+ streams:
+ type: str
+---
+name: mapping11
+desc: Test keyword regex default matching-rule any
+data:
+ foobar1: 1
+ foobar2: 2
+ bar2: 3
+schema:
+ type: map
+ mapping:
+ regex;([1-2]$):
+ type: int
+ regex;(^foobar):
+ type: int
+---
+name: mapping12
+desc: Test keyword regex declared matching-rule any
+data:
+ foobar1: 1
+ foobar2: 2
+ bar2: 3
+schema:
+ type: map
+ matching-rule: 'any'
+ mapping:
+ regex;([1-2]$):
+ type: int
+ regex;(^foobar):
+ type: int
+---
+name: mapping13
+desc: Test keyword regex declared matching-rule all
+data:
+ foobar1: 1
+ foobar2: 2
+ foobar3: 3
+schema:
+ type: map
+ matching-rule: 'all'
+ mapping:
+ regex;([1-3]$):
+ type: int
+ regex;(^foobar):
+ type: int
+---
+name: mapping14
+desc: Test mixed keyword regex and normal keyword
+data:
+ standard:
+ FRIST-800-53
+ AU-1:
+ family: AU
+ name: Audit and Accountability Policy and Procedures
+schema:
+ type: map
+ mapping:
+ regex;/[A-Z]-/:
+ type: map
+ mapping:
+ name:
+ type: str
+ family:
+ type: str
+ required: True
+ standard:
+ type: str
+---
+name: mapping-default-1
+desc: Test that default mapping keyword works out of the box in a good case
+data:
+ OWNERSHIP:
+ - code: 1
+ key: BLM-BURNS
+ alias: BLM-BURNS
+ WHT: foo
+schema:
+ type: map
+ mapping:
+ WHT:
+ type: str
+ =:
+ type: seq
+ required: true
+ sequence:
+ - type: map
+ mapping:
+ 'code':
+ type: int
+ required: true
+ unique: true
+ 'key':
+ type: str
+ required: true
+ 'alias':
+ type: str
+ required: true
+---
+name: mapping-default-2
+desc: default value of map with number type and no other key matching
+#
+schema:
+ type: map
+ mapping:
+ =:
+ type: number
+ range:
+ min: -10
+ max: 10
+#
+data:
+ value1: 0
+ value2: 10
+ value3: -10
+---
+name: mapping17
+desc: Test that allowempty works without specifying mapping keyword when used inside a sequence block
+data:
+ rally:
+ plugins:
+ - netcreate-boot: rally/rally-plugins/netcreate-boot
+schema:
+ type: map
+ mapping:
+ rally:
+ type: map
+ allowempty: True
+ mapping:
+ plugins:
+ type: seq
+ sequence:
+ - type: map
+ allowempty: True
+---
+name: mapping18
+desc: Test that regexes can be 'required'
+data:
+ person1: Jack
+ person2: Fred
+schema:
+ type: map
+ mapping:
+ regex;(person[1-9]):
+ required: True \ No newline at end of file
diff --git a/tests/files/success/test_matching.yaml b/tests/files/success/test_matching.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_matching.yaml
diff --git a/tests/files/success/test_merge.yaml b/tests/files/success/test_merge.yaml
new file mode 100644
index 0000000..1e0a94d
--- /dev/null
+++ b/tests/files/success/test_merge.yaml
@@ -0,0 +1,36 @@
+##
+---
+name: merge1
+desc: merge maps
+#
+schema:
+ type: map
+ mapping:
+ "group":
+ type: map
+ mapping:
+ "name": &name
+ type: str
+ required: True
+ "email": &email
+ type: str
+ pattern: .+@.+
+ required: False
+ "user":
+ type: map
+ mapping:
+ "name":
+ <<: *name # merge
+ length:
+ max: 16 # add
+ "email":
+ <<: *email # merge
+ required: True # override
+#
+data:
+ group:
+ name: foo
+ email: foo@mail.com
+ user:
+ name: bar
+ email: bar@mail.com
diff --git a/tests/files/success/test_name.yaml b/tests/files/success/test_name.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_name.yaml
diff --git a/tests/files/success/test_nullable.yaml b/tests/files/success/test_nullable.yaml
new file mode 100644
index 0000000..df8ddf8
--- /dev/null
+++ b/tests/files/success/test_nullable.yaml
@@ -0,0 +1,11 @@
+---
+name: nullable1
+desc: Test that nullable works
+data:
+ name:
+schema:
+ type: map
+ mapping:
+ name:
+ type: str
+ nullable: True
diff --git a/tests/files/success/test_pattern.yaml b/tests/files/success/test_pattern.yaml
new file mode 100644
index 0000000..41a751b
--- /dev/null
+++ b/tests/files/success/test_pattern.yaml
@@ -0,0 +1,18 @@
+---
+name: pattern1
+desc: Test simples pattern
+data: foo@gmail.com
+schema:
+ type: str
+ pattern: .+@.+
+---
+name: pattern2
+desc: Test simple pattern in list
+data:
+ - foo@mail.com
+ - bar@mail.net
+schema:
+ type: seq
+ sequence:
+ - type: str
+ pattern: .+@.+
diff --git a/tests/files/success/test_range.yaml b/tests/files/success/test_range.yaml
new file mode 100644
index 0000000..ef3c18a
--- /dev/null
+++ b/tests/files/success/test_range.yaml
@@ -0,0 +1,166 @@
+##
+---
+name: range1
+desc: range test && bug#?????
+#
+schema:
+ type: map
+ mapping:
+ "max-only":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {max: 100}
+ "min-only":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {min: 10.0}
+ "max-and-min":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {max: 100.0, min: 10.0}
+#
+data:
+ max-only:
+ - 100
+ - 100.0
+ min-only:
+ - 10
+ - 10.0
+ max-and-min:
+ - 100
+ - 10
+ - 100.0
+ - 10.0
+##
+---
+name: range2
+desc: range test (with max-ex and min-ex)
+#
+schema:
+ type: map
+ mapping:
+ "max-ex-only":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {max-ex: 100}
+ "min-ex-only":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {min-ex: 10.0}
+ "max-ex-and-min-ex":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {max-ex: 100.0, min-ex: 10.0}
+#
+data:
+ max-ex-only:
+ - 99
+ - 99.99999
+ min-ex-only:
+ - 11
+ - 10.00001
+ max-ex-and-min-ex:
+ - 99
+ - 11
+ - 99.99999
+ - 10.00001
+##
+---
+name: range3
+desc: range test (with max, min, max-ex and min-ex)
+#
+schema:
+ type: map
+ mapping:
+ "A":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {max: 100, min-ex: 10.0}
+ "B":
+ type: seq
+ sequence:
+ - type: number
+ required: True
+ range: {min: 10, max-ex: 100.0}
+#
+data:
+ A:
+ - 100
+ - 10.00001
+ B:
+ - 10
+ - 99.99999
+---
+name: range4
+desc: Test range min/max works with map size
+data:
+ foo: bar
+schema:
+ type: map
+ range:
+ min: 1
+ max: 3
+ mapping:
+ foo:
+ type: str
+---
+name: range5
+desc: Test range works with seq
+data:
+ - foobar
+ - barfoo
+schema:
+ type: seq
+ range:
+ min: 1
+ max: 3
+ sequence:
+ - type: str
+---
+name: range6
+desc: test range on float type
+data:
+ 2.0
+schema:
+ type: float
+ range:
+ min: 1
+ max: 3
+---
+name: range7
+desc: Test range on float with negative boundary
+data:
+ -0.9
+schema:
+ type: float
+ range:
+ min: -1
+ max: 1.0
+---
+name: range8
+desc: Test range min-ex & max-ex
+data:
+ - 20
+ - 25
+ - 29
+schema:
+ type: seq
+ sequence:
+ - type: int
+ range:
+ max-ex: 30
+ min-ex: 18
diff --git a/tests/files/success/test_required.yaml b/tests/files/success/test_required.yaml
new file mode 100644
index 0000000..9787843
--- /dev/null
+++ b/tests/files/success/test_required.yaml
@@ -0,0 +1,15 @@
+---
+name: required1
+desc: Test that req and required works
+data:
+ name: foo
+ foo: bar
+schema:
+ type: map
+ mapping:
+ name:
+ type: str
+ req: True
+ foo:
+ type: str
+ required: True
diff --git a/tests/files/success/test_schema.yaml b/tests/files/success/test_schema.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_schema.yaml
diff --git a/tests/files/success/test_sequence.yaml b/tests/files/success/test_sequence.yaml
new file mode 100644
index 0000000..3a4491f
--- /dev/null
+++ b/tests/files/success/test_sequence.yaml
@@ -0,0 +1,44 @@
+##
+---
+name: sequence1
+desc: sequence test
+#
+schema:
+ type: seq
+ sequence:
+ - type: str
+data:
+ - foo
+ - bar
+ - baz
+---
+name: sequence2
+desc: test sequence showrtcut
+schema:
+ type: seq
+ seq:
+ - type: str
+data:
+ - foo
+ - bar
+ - baz
+---
+name: sequence3
+desc: Test that you do not have to specify type seq when keyword sequence is present
+data:
+ - foo
+ - bar
+ - foobar
+schema:
+ sequence:
+ - type: str
+---
+name: sequence4
+desc: Test that you do not have to specify type seq when keyword seq is present
+data:
+ - foo
+ - bar
+ - foobar
+schema:
+ seq:
+ - type: str
diff --git a/tests/files/success/test_sequence_multi.yaml b/tests/files/success/test_sequence_multi.yaml
new file mode 100644
index 0000000..432145c
--- /dev/null
+++ b/tests/files/success/test_sequence_multi.yaml
@@ -0,0 +1,64 @@
+---
+name: seq-multi-1
+desc: Test that multiple sequence values is supported
+data:
+ - "foo"
+ - 123
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: str
+ - type: int
+---
+name: seq-multi-2
+desc: Test that multiple sequence values with matching 'all' is supported
+data:
+ - "foo"
+ - "123"
+schema:
+ type: seq
+ matching: "all"
+ seq:
+ - type: str
+ - type: str
+---
+name: seq-multi-3
+desc: Test that multiple sequence values with matching '*' is supported
+data:
+ - "foo"
+schema:
+ type: seq
+ matching: "*"
+ seq:
+ - type: bool
+ - type: int
+---
+name: seq-multi-4
+desc: Test that multiple sequence values with nested data structures work
+data:
+ - foo: 123
+ - "foobar"
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: str
+ - type: map
+ mapping:
+ foo:
+ type: int
+---
+name: sq-multi-5
+desc: Test that multiple sequence vlaues with nested lists works
+data:
+ - - 123
+ - "foobar"
+schema:
+ type: seq
+ matching: "any"
+ seq:
+ - type: str
+ - type: seq
+ sequence:
+ - type: int
diff --git a/tests/files/success/test_type_any.yaml b/tests/files/success/test_type_any.yaml
new file mode 100644
index 0000000..cc015e4
--- /dev/null
+++ b/tests/files/success/test_type_any.yaml
@@ -0,0 +1,27 @@
+---
+name: type-any-1
+desc: test simples case of any type
+data: abc
+schema:
+ type: any
+---
+name: type-any-2
+desc: test any type inside sequence
+data:
+ - abc
+ - 123
+ - 3.14159
+ - True
+schema:
+ type: seq
+ sequence:
+ - type: any
+---
+name: type-any-3
+desc: test any type validates a dict
+data:
+ foobar:
+ barfoo:
+ opa: 1337
+schema:
+ type: any
diff --git a/tests/files/success/test_type_bool.yaml b/tests/files/success/test_type_bool.yaml
new file mode 100644
index 0000000..4857690
--- /dev/null
+++ b/tests/files/success/test_type_bool.yaml
@@ -0,0 +1,39 @@
+---
+name: bool1
+desc: Test simples bool value
+data: True
+schema:
+ type: bool
+---
+name: bool2
+desc: Test bool value inside list
+data:
+ - True
+ - False
+schema:
+ type: seq
+ sequence:
+ - type: bool
+---
+name: bool3
+desc: Test bool value in mapping
+data:
+ foo: True
+schema:
+ type: map
+ mapping:
+ foo:
+ type: bool
+---
+name: bool4
+desc: Test bool inside nested map & seq
+data:
+ foo:
+ - True
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: bool
diff --git a/tests/files/success/test_type_date.yaml b/tests/files/success/test_type_date.yaml
new file mode 100644
index 0000000..b6166e6
--- /dev/null
+++ b/tests/files/success/test_type_date.yaml
@@ -0,0 +1,40 @@
+---
+name: type-date-1
+desc: basic test for date type with default formats
+data: "2017-01-01"
+schema:
+ type: date
+---
+name: type-date-2
+desc: Basic test for date type with defined date-formats
+data: "31-01-2017"
+schema:
+ type: date
+ format: "%d-%m-%Y"
+---
+name: type-date-3
+desc: Basic test for date type with defined date-formats
+data:
+ - "31-01-2017"
+ - "2017-01-31"
+schema:
+ type: seq
+ sequence:
+ - type: date
+ format:
+ - "%d-%m-%Y"
+ - "%Y-%m-%d"
+---
+name: type-data-4
+desc: Basic test for many different possible values with default formats
+data:
+ - '1997'
+ - '1997-07'
+ - '1997-07-16'
+ - '1997-07-16T19:20+01:00'
+ - '1997-07-16T19:20:30+01:00'
+ - '1997-07-16T19:20:30.45+01:00'
+schema:
+ type: seq
+ sequence:
+ - type: date
diff --git a/tests/files/success/test_type_enum.yaml b/tests/files/success/test_type_enum.yaml
new file mode 100644
index 0000000..bde9bd7
--- /dev/null
+++ b/tests/files/success/test_type_enum.yaml
@@ -0,0 +1,43 @@
+---
+name: type-enum-1
+desc: Test the most basic case for enum
+data: C
+schema:
+ type: str
+ enum: [A, B, C, D, E]
+---
+name: type-enum-2
+desc: Test bool value inside list
+data:
+ - B
+ - C
+schema:
+ type: seq
+ sequence:
+ - type: str
+ enum: [A, B, C, D, E]
+---
+name: type-enum-3
+desc: Test bool value in mapping
+data:
+ foo: A
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+ enum: [A, B, C, D, E]
+---
+name: type-enum-4
+desc: Test bool inside nested map & seq
+data:
+ foo:
+ - C
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: str
+ enum: [A, B, C, D, E]
diff --git a/tests/files/success/test_type_float.yaml b/tests/files/success/test_type_float.yaml
new file mode 100644
index 0000000..ffd1bba
--- /dev/null
+++ b/tests/files/success/test_type_float.yaml
@@ -0,0 +1,43 @@
+---
+name: float1
+desc: Test simples float value
+data: 3.14159
+schema:
+ type: float
+---
+name: float2
+desc: Test float value inside list
+data:
+ - 1
+ - 3.14159
+schema:
+ type: seq
+ sequence:
+ - type: float
+---
+name: float3
+desc: Test float value in mapping
+data:
+ foo: 3.14159
+ bar: 1
+schema:
+ type: map
+ mapping:
+ foo:
+ type: float
+ bar:
+ type: float
+---
+name: float4
+desc: Test float inside nested map & seq
+data:
+ foo:
+ - 1
+ - 3.14159
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: float
diff --git a/tests/files/success/test_type_int.yaml b/tests/files/success/test_type_int.yaml
new file mode 100644
index 0000000..93dd805
--- /dev/null
+++ b/tests/files/success/test_type_int.yaml
@@ -0,0 +1,38 @@
+---
+name: int1
+desc: Test simples int value
+data: 1
+schema:
+ type: int
+---
+name: int2
+desc: Test int value inside list
+data:
+ - 1
+schema:
+ type: seq
+ sequence:
+ - type: int
+---
+name: int3
+desc: Test int value in mapping
+data:
+ foo: 1
+schema:
+ type: map
+ mapping:
+ foo:
+ type: int
+---
+name: int4
+desc: Test int inside nested map & seq
+data:
+ foo:
+ - 1
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: int
diff --git a/tests/files/success/test_type_map.yaml b/tests/files/success/test_type_map.yaml
new file mode 100644
index 0000000..010b0e9
--- /dev/null
+++ b/tests/files/success/test_type_map.yaml
@@ -0,0 +1,39 @@
+---
+name: type-map-1
+desc: Test the most basic case for map
+data:
+ foo: bar
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+---
+name: type-map-2
+desc:
+data:
+ - foo: bar
+ - foo: bar
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: str
+---
+name: type-map-3
+desc: Test bool inside nested map & seq
+data:
+ foo:
+ - bar: foobar
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ bar:
+ type: str
diff --git a/tests/files/success/test_type_none.yaml b/tests/files/success/test_type_none.yaml
new file mode 100644
index 0000000..db65652
--- /dev/null
+++ b/tests/files/success/test_type_none.yaml
@@ -0,0 +1,47 @@
+#
+# NOTE: This case is not allowed becuase Core class do NOT allow
+# there is no data to validate. This happens if None is at top level
+# of the data structure.
+#
+# ---
+# name: type-none-1
+# desc: Most basic test for type None
+# data: ~
+# schema:
+# type: none
+---
+name: type-none-2
+desc: Test that none type works with none type as value in map
+data:
+ name: ~
+schema:
+ type: map
+ mapping:
+ name:
+ type: none
+---
+name: type-none-3
+desc: Test that none type works as value in sequence
+data:
+ - ~
+ - ~
+schema:
+ type: seq
+ sequence:
+ - type: none
+---
+name: type-none-4
+desc: Test that none type works inside nested map, seq, map
+data:
+ foo:
+ - bar: ~
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ bar:
+ type: none
diff --git a/tests/files/success/test_type_number.yaml b/tests/files/success/test_type_number.yaml
new file mode 100644
index 0000000..f0b5cb4
--- /dev/null
+++ b/tests/files/success/test_type_number.yaml
@@ -0,0 +1,76 @@
+---
+name: type-number-1
+desc: Most basic test for type number
+data: '1337.0'
+schema:
+ type: number
+---
+name: type-number-2
+desc: Test that number type works with as value in map
+data:
+ foo: 1337
+ bar: 3.14159
+ qwe: 0.0
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: number
+ bar:
+ type: number
+ qwe:
+ type: number
+ rty:
+ type: number
+ ewq:
+ type: number
+ dsa:
+ type: number
+---
+name: type-number-3
+desc: Test that different number values works as values in seq
+data:
+ - 1337
+ - 3.14159
+ - 0.0
+ - '1337'
+ - '3.14159'
+ - '0.0'
+schema:
+ type: seq
+ sequence:
+ - type: number
+---
+name: type-number-4
+desc: Test that number type works inside nested map, seq, map
+data:
+ foobar:
+ - foo: 1337
+ bar: 3.14159
+ qwe: 0.0
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foobar:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: number
+ bar:
+ type: number
+ qwe:
+ type: number
+ rty:
+ type: number
+ ewq:
+ type: number
+ dsa:
+ type: number
diff --git a/tests/files/success/test_type_scalar.yaml b/tests/files/success/test_type_scalar.yaml
new file mode 100644
index 0000000..da34401
--- /dev/null
+++ b/tests/files/success/test_type_scalar.yaml
@@ -0,0 +1,76 @@
+---
+name: type-scalar-1
+desc: Most basic test for type scalar
+data: '1337.0'
+schema:
+ type: scalar
+---
+name: type-scalar-2
+desc: Test that scalar type works with as value in map
+data:
+ foo: 1337
+ bar: 3.14159
+ qwe: True
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: scalar
+ bar:
+ type: scalar
+ qwe:
+ type: scalar
+ rty:
+ type: scalar
+ ewq:
+ type: scalar
+ dsa:
+ type: scalar
+---
+name: type-scalar-3
+desc: Test that different scalar values works as values in seq
+data:
+ - 1337
+ - 3.14159
+ - True
+ - '1337'
+ - '3.14159'
+ - '0.0'
+schema:
+ type: seq
+ sequence:
+ - type: scalar
+---
+name: type-scalar-4
+desc: Test that scalar type works inside nested map, seq, map
+data:
+ foobar:
+ - foo: 1337
+ bar: 3.14159
+ qwe: True
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foobar:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: scalar
+ bar:
+ type: scalar
+ qwe:
+ type: scalar
+ rty:
+ type: scalar
+ ewq:
+ type: scalar
+ dsa:
+ type: scalar
diff --git a/tests/files/success/test_type_seq.yaml b/tests/files/success/test_type_seq.yaml
new file mode 100644
index 0000000..5eb1b16
--- /dev/null
+++ b/tests/files/success/test_type_seq.yaml
@@ -0,0 +1,36 @@
+---
+name: type-seq-1
+desc: Test the most basic case for seq
+data:
+ - foo
+schema:
+ type: seq
+ sequence:
+ - type: str
+---
+name: type-seq-2
+desc: Test that seq in seq works
+data:
+ - - True
+ - - False
+schema:
+ type: seq
+ sequence:
+ - type: seq
+ sequence:
+ - type: bool
+---
+name: type-seq-3
+desc: Test bool inside nested map & seq
+data:
+ - foo:
+ - True
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: bool
diff --git a/tests/files/success/test_type_str.yaml b/tests/files/success/test_type_str.yaml
new file mode 100644
index 0000000..e5717c9
--- /dev/null
+++ b/tests/files/success/test_type_str.yaml
@@ -0,0 +1,56 @@
+---
+name: str1
+desc: Test simples str value
+data: "foobar"
+schema:
+ type: str
+---
+name: str2
+desc: Test str value inside list
+data:
+ - 'foo'
+ - bar
+schema:
+ type: seq
+ sequence:
+ - type: str
+---
+name: str3
+desc: Test str value in mapping
+data:
+ foo: 'foobar'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: str
+---
+name: str4
+desc: Test str inside nested map & seq
+data:
+ foo:
+ - 'foo'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: seq
+ sequence:
+ - type: str
+---
+name: deftype1
+desc: default type test. Becuase str is the default type it is in this file.
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ "email":
+#
+data:
+ - name: foo
+ email: foo@mail.com
+ - name: bar
+ - email: baz@mail.com
diff --git a/tests/files/success/test_type_symbol.yaml b/tests/files/success/test_type_symbol.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/files/success/test_type_symbol.yaml
diff --git a/tests/files/success/test_type_text.yaml b/tests/files/success/test_type_text.yaml
new file mode 100644
index 0000000..7f218e2
--- /dev/null
+++ b/tests/files/success/test_type_text.yaml
@@ -0,0 +1,75 @@
+---
+name: type-text-1
+desc: Test simples text type
+data: "foobar"
+schema:
+ type: text
+---
+name: type-text-2
+desc: Test possible values as values in seq
+data:
+ - 'abc'
+ - '1337'
+ - '3.14159'
+ - 1337
+ - 3.14159
+schema:
+ type: seq
+ sequence:
+ - type: text
+---
+name: type-text-3
+desc: Test possible values as values in map
+data:
+ foo: 1337
+ bar: 3.14159
+ qwe: 'abc'
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foo:
+ type: text
+ bar:
+ type: text
+ qwe:
+ type: text
+ rty:
+ type: text
+ ewq:
+ type: text
+ dsa:
+ type: text
+---
+name: type-text-4
+desc: Test that text type works inside nested map, seq, map
+data:
+ foobar:
+ - foo: 1337
+ bar: 3.14159
+ qwe: 'abc'
+ rty: '1337'
+ ewq: '3.14159'
+ dsa: '0.0'
+schema:
+ type: map
+ mapping:
+ foobar:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ foo:
+ type: text
+ bar:
+ type: text
+ qwe:
+ type: text
+ rty:
+ type: text
+ ewq:
+ type: text
+ dsa:
+ type: text
diff --git a/tests/files/success/test_type_timestamp.yaml b/tests/files/success/test_type_timestamp.yaml
new file mode 100644
index 0000000..5bcfbcc
--- /dev/null
+++ b/tests/files/success/test_type_timestamp.yaml
@@ -0,0 +1,37 @@
+---
+name: type-timestamp-1
+desc: Most basic timestamp test
+data: "2015-03-29T18:45:00+00:00"
+schema:
+ type: timestamp
+---
+name: type-timestamp-2
+desc: Test timestamps as values in seq
+data:
+ - "2015-03-29T18:45:00+00:00"
+ - "2015-03-29T18:45:00"
+ - "2015-03-29T11:45:00 -0700"
+ - "2015-03-29"
+schema:
+ type: seq
+ sequence:
+ - type: timestamp
+---
+name: type-timestamp-3
+desc: Basic test of different types of timestamps
+data:
+ d1: "2015-03-29T18:45:00+00:00"
+ d2: "2015-03-29T18:45:00"
+ d3: "2015-03-29T11:45:00 -0700"
+ d4: "2015-03-29"
+schema:
+ type: map
+ mapping:
+ d1:
+ type: timestamp
+ d2:
+ type: timestamp
+ d3:
+ type: timestamp
+ d4:
+ type: timestamp
diff --git a/tests/files/success/test_unique.yaml b/tests/files/success/test_unique.yaml
new file mode 100644
index 0000000..20cb64f
--- /dev/null
+++ b/tests/files/success/test_unique.yaml
@@ -0,0 +1,138 @@
+---
+name: unique1
+desc: unique constraint test with map
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ unique: true
+ "age":
+ type: int
+#
+data:
+ - name: foo
+ age: 10
+ - name: bar
+ age: 10
+ - name: baz
+ age: 10
+---
+name: unique2
+desc: unique constraint test with seq
+#
+schema:
+ type: seq
+ sequence:
+ - type: str
+ unique: true
+#
+data:
+ - foo
+ - ~
+ - bar
+ - ~
+ - baz
+---
+name: unique3
+desc: unique constraint and '<<' (merge)
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ type: str
+ required: True
+ unique: true
+ "value":
+ type: any
+ required: True
+#
+data:
+ # no sense
+ - name: x1
+ value: 10
+ - name: x2
+ value: 20
+---
+name: unique4
+desc: unique constrant and anchor
+#
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "name":
+ type: str
+ "value":
+ type: any
+#
+data:
+ # no sense
+ - name: x1
+ value: 10
+ - name: x2
+ value: 20
+---
+name: unique5
+desc: unique constring in nested data structures
+data:
+ - name: foo
+ email: admin@mail.com
+ groups:
+ - users
+ - foo
+ - admin
+ - name: bar
+ email: admin@mail.com
+ groups:
+ - users
+ - admin
+ - name: baz
+ email: baz@mail.com
+ groups:
+ - users
+schema:
+ type: seq
+ sequence:
+ - type: map
+ required: True
+ mapping:
+ name:
+ type: str
+ required: True
+ unique: True
+ email:
+ type: str
+ groups:
+ type: seq
+ sequence:
+ - type: str
+ unique: True
+---
+name: unique6
+desc: Test that unique do not fail when the key it tries to lookup is missing
+data:
+ - xref: 'GOC:hm'
+ uri: 'GOC:hm'
+ - uri: 'http://orcid.org/0000-0002-4862-3181'
+schema:
+ type: seq
+ sequence:
+ - type: map
+ mapping:
+ "uri":
+ type: str
+ required: true
+ unique: true
+ pattern: ^((ht|f)tp(s?)\:\/\/\w[\/\.\-\:\w]+)|(GOC\:[\w\_]+)$
+ "xref":
+ type: str
+ required: false
+ unique: true
+ pattern: ^\w+:\w+$
diff --git a/tests/files/success/test_version.yaml b/tests/files/success/test_version.yaml
new file mode 100644
index 0000000..32621ba
--- /dev/null
+++ b/tests/files/success/test_version.yaml
@@ -0,0 +1,5 @@
+---
+data: foo
+schema:
+ version: 1.0.0
+ type: str
diff --git a/tests/files/unicode/1f.yaml b/tests/files/unicode/1f.yaml
new file mode 100644
index 0000000..97d51f5
--- /dev/null
+++ b/tests/files/unicode/1f.yaml
@@ -0,0 +1,13 @@
+schema:
+ type: map
+ mapping:
+ msg:
+ type: int
+ Alô:
+ type: int
+data:
+ msg: "Alô do Brasil!!"
+ Alô: "Brasil!!"
+errors:
+ - "Value 'Alô do Brasil!!' is not of type 'int'. Path: '/msg'"
+ - "Value 'Brasil!!' is not of type 'int'. Path: '/Alô'"
diff --git a/tests/files/unicode/1s.yaml b/tests/files/unicode/1s.yaml
new file mode 100644
index 0000000..ab9fff9
--- /dev/null
+++ b/tests/files/unicode/1s.yaml
@@ -0,0 +1,10 @@
+schema:
+ type: map
+ mapping:
+ msg:
+ type: str
+ Alô:
+ type: str
+data:
+ msg: "Alô do Brasil!!"
+ Alô: "Brasil!!"
diff --git a/tests/files/unicode/3f.yaml b/tests/files/unicode/3f.yaml
new file mode 100644
index 0000000..a963f1b
--- /dev/null
+++ b/tests/files/unicode/3f.yaml
@@ -0,0 +1,10 @@
+schema:
+ type: seq
+ sequence:
+ - type: int
+data:
+ - foobar
+ - åäö
+errors:
+ - "Value 'foobar' is not of type 'int'. Path: '/0'"
+ - "Value 'åäö' is not of type 'int'. Path: '/1'"
diff --git a/tests/files/unicode/3s.yaml b/tests/files/unicode/3s.yaml
new file mode 100644
index 0000000..3ab54de
--- /dev/null
+++ b/tests/files/unicode/3s.yaml
@@ -0,0 +1,7 @@
+schema:
+ type: seq
+ sequence:
+ - type: str
+data:
+ - foobar
+ - åäö
diff --git a/tests/test_cli.py b/tests/test_cli.py
new file mode 100644
index 0000000..33da698
--- /dev/null
+++ b/tests/test_cli.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+
+# python std lib
+import os
+import sys
+
+# pykwalify package imports
+from pykwalify import cli
+
+
+class TestCLI(object):
+
+ def test_cli(self, tmpdir):
+ """
+ Test that when passing in certain arguments from commandline they
+ are handled correctly by docopt and correct args structure is returned.
+ """
+ input = tmpdir.join("cli/1a.yaml")
+ schema_file = tmpdir.join("cli/1b.yaml")
+
+ sys.argv = [
+ 'scripts/pykwalify',
+ '-d', str(input),
+ '-s', str(schema_file),
+ '-v'
+ ]
+
+ expected = {
+ '--data-file': str(input),
+ '--schema-file': [str(schema_file)],
+ '--quiet': False,
+ '--verbose': 1,
+ }
+
+ cli_args = cli.parse_cli()
+
+ for k, v in expected.items():
+ assert k in cli_args
+ assert cli_args[k] == expected[k]
+
+ def f(self, *args):
+ """
+ Returns abs path to test files inside tests/files/
+ """
+ return os.path.join(os.path.dirname(os.path.realpath(__file__)), "files", *args)
+
+ def test_run_cli(self):
+ """
+ This should test that running the cli still works as expected
+ """
+ input = self.f("cli/1a.yaml")
+ schema_file = self.f("cli/1b.yaml")
+
+ sys.argv = [
+ 'scripts/pykwalify',
+ '-d', str(input),
+ '-s', str(schema_file),
+ ]
+
+ cli_args = cli.parse_cli()
+ c = cli.run(cli_args)
+ assert c.validation_errors == []
diff --git a/tests/test_core.py b/tests/test_core.py
new file mode 100644
index 0000000..6fcd81e
--- /dev/null
+++ b/tests/test_core.py
@@ -0,0 +1,572 @@
+# -*- coding: utf-8 -*-
+
+""" Unit test for pyKwalify - Core """
+
+# python std lib
+import os
+
+# pykwalify imports
+import pykwalify
+from pykwalify.core import Core
+from pykwalify.errors import SchemaError, CoreError
+
+# 3rd party imports
+import pytest
+from pykwalify.compat import yaml
+from testfixtures import compare
+
+
+class TestCore(object):
+
+ def setUp(self):
+ pykwalify.partial_schemas = {}
+
+ def f(self, *args):
+ return os.path.join(os.path.dirname(os.path.realpath(__file__)), "files", *args)
+
+ def test_create_empty_core_object(self, tmpdir):
+ """
+ If createing a core object without any source or schema file an exception should be raised.
+ """
+ with pytest.raises(CoreError) as ex:
+ Core()
+ assert "No source file/data was loaded" in str(ex.value)
+
+ # To trigger schema exception we must pass in a source file
+ source_f = tmpdir.join("bar.json")
+ source_f.write("3.14159")
+
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=str(source_f))
+ assert "No schema file/data was loaded" in str(ex.value)
+
+ def test_load_non_existing_file(self):
+ file_to_load = "/tmp/foo/bar/barfoo"
+ assert not os.path.exists(file_to_load), "Following file cannot exists on your system while running these tests : {0}".format(file_to_load)
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=file_to_load)
+ assert "Provided source_file do not exists on disk" in str(ex.value)
+
+ def test_load_non_existsing_schema_file(self):
+ """
+ Exception should be raised if the specefied schema file do not exists on disk.
+ """
+ file_to_load = "/tmp/foo/bar/barfoo"
+ assert not os.path.exists(file_to_load), "Following file cannot exists on your system while running these tests : {0}".format(file_to_load)
+ with pytest.raises(CoreError) as ex:
+ Core(schema_files=[file_to_load])
+ assert "Provided source_file do not exists on disk" in str(ex.value)
+
+ def test_load_wrong_schema_files_type(self):
+ """
+ It should only be possible to send in a list type as 'schema_files' object
+ """
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=None, schema_files={})
+ assert "schema_files must be of list type" in str(ex.value)
+
+ def test_load_json_file(self, tmpdir):
+ """
+ Load source & schema files that has json file ending.
+ """
+ source_f = tmpdir.join("bar.json")
+ source_f.write("3.14159")
+
+ schema_f = tmpdir.join("foo.json")
+ schema_f.write('{"type": "float"}')
+
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+
+ # TODO: Try to load a non existing json file
+
+ def test_load_yaml_files(self, tmpdir):
+ """
+ Load source & schema files that has yaml file ending.
+ """
+ source_f = tmpdir.join("foo.yaml")
+ source_f.write("3.14159")
+
+ schema_f = tmpdir.join("bar.yaml")
+ schema_f.write("type: float")
+
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+
+ def test_load_unsupported_format(self, tmpdir):
+ """
+ Try to load some fileending that is not supported.
+ Currently XML is not supported.
+ """
+ source_f = tmpdir.join("foo.xml")
+ source_f.write("<foo>bar</foo>")
+
+ schema_f = tmpdir.join("bar.xml")
+ schema_f.write("<foo>bar</foo>")
+
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=str(source_f))
+ assert "Unable to load source_file. Unknown file format of specified file path" in str(ex.value)
+
+ with pytest.raises(CoreError) as ex:
+ Core(schema_files=[str(schema_f)])
+ assert "Unknown file format. Supported file endings is" in str(ex.value)
+
+ def test_load_empty_json_file(self, tmpdir):
+ """
+ Loading an empty json files should raise an exception
+ """
+ # Load empty source file
+ source_f = tmpdir.join("foo.json")
+ source_f.write("")
+
+ schema_f = tmpdir.join("bar.json")
+ schema_f.write("")
+
+ with pytest.raises(ValueError) as ex:
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+ # Python 2.7 and Python 3.5 JSON parsers return different exception
+ # strings for the same data file, so check for both errors strings.
+ assert ("No JSON object could be decoded" in str(ex.value) or
+ "Expecting value:" in str(ex.value))
+
+ # Load empty schema files
+ source_f = tmpdir.join("foo.json")
+ source_f.write("3.14159")
+
+ schema_f = tmpdir.join("bar.json")
+ schema_f.write("")
+
+ with pytest.raises(ValueError) as ex:
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+ assert ("No JSON object could be decoded" in str(ex.value) or
+ "Expecting value:" in str(ex.value))
+
+ def test_load_empty_yaml_file(self, tmpdir):
+ """
+ Loading empty yaml files should raise an exception
+ """
+ # Load empty source file
+ source_f = tmpdir.join("foo.yaml")
+ source_f.write("")
+
+ schema_f = tmpdir.join("bar.yaml")
+ schema_f.write("")
+
+ # TODO: This is abit buggy because wrong exception is raised...
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+ # assert "Unable to load any data from source yaml file" in str(ex.value)
+
+ # Load empty schema files
+ source_f = tmpdir.join("foo.yaml")
+ source_f.write("3.14159")
+
+ schema_f = tmpdir.join("bar.yaml")
+ schema_f.write("")
+
+ with pytest.raises(CoreError) as ex:
+ Core(source_file=str(source_f), schema_files=[str(schema_f)])
+ assert "No data loaded from file" in str(ex.value)
+
+ def test_validation_error_but_not_raise_exception(self):
+ """
+ Test that if 'raise_exception=False' when validating that no exception is raised.
+
+ Currently file 2a.yaml & 2b.yaml is designed to cause exception.
+ """
+ c = Core(source_file=self.f("cli", "2a.yaml"), schema_files=[self.f("cli", "2b.yaml")])
+ c.validate(raise_exception=False)
+
+ assert c.validation_errors == [
+ "Value '1' is not of type 'str'. Path: '/0'", "Value '2' is not of type 'str'. Path: '/1'", "Value '3' is not of type 'str'. Path: '/2'"
+ ]
+
+ # TODO: Fix this issue...
+ # assert ('pykwalify.core', 'ERROR', 'Errors found but will not raise exception...') in l.actual()
+
+ def test_core_data_mode(self):
+ Core(source_data=3.14159, schema_data={"type": "number"}).validate()
+ Core(source_data="1e-06", schema_data={"type": "float"}).validate()
+ Core(source_data=3.14159, schema_data={"type": "float"}).validate()
+ Core(source_data=3, schema_data={"type": "float"}).validate()
+ Core(source_data=3, schema_data={"type": "int"}).validate()
+ Core(source_data=True, schema_data={"type": "bool"}).validate()
+ Core(source_data="foobar", schema_data={"type": "str"}).validate()
+ Core(source_data="foobar", schema_data={"type": "text"}).validate()
+ Core(source_data="foobar", schema_data={"type": "any"}).validate()
+
+ # Test that 'any' allows types that is not even implemented
+ def foo():
+ pass
+ Core(source_data=foo, schema_data={"type": "any"}).validate()
+ Core(source_data=lambda x: x, schema_data={"type": "any"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data="1z-06", schema_data={"type": "float"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data="abc", schema_data={"type": "number"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data=3.14159, schema_data={"type": "int"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data=1337, schema_data={"type": "bool"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data=1, schema_data={"type": "str"}).validate()
+
+ with pytest.raises(SchemaError):
+ Core(source_data=True, schema_data={"type": "text"}).validate()
+
+ def test_multi_file_support(self):
+ """
+ This should test that multiple files is supported correctly
+ """
+ pass_tests = [
+ # Test that include directive can be used at top level of the schema
+ (
+ [
+ self.f("partial_schemas", "1s-schema.yaml"),
+ self.f("partial_schemas", "1s-partials.yaml"),
+ ],
+ self.f("partial_schemas", "1s-data.yaml"),
+ {
+ 'sequence': [{'include': 'fooone'}],
+ 'type': 'seq',
+ }
+ ),
+ # # This test that include directive works inside sequence
+ # ([self.f("33a.yaml"), self.f("33b.yaml")], self.f("33c.yaml"), {'sequence': [{'include': 'fooone'}], 'type': 'seq'}),
+ # This test recursive schemas
+ (
+ [
+ self.f("partial_schemas", "2s-schema.yaml"),
+ self.f("partial_schemas", "2s-partials.yaml"),
+ ],
+ self.f("partial_schemas", "2s-data.yaml"),
+ {
+ 'sequence': [{'include': 'fooone'}],
+ 'type': 'seq',
+ }
+ ),
+ # This tests that you can include a partial schema alongside other rules in a map
+ (
+ [
+ self.f("partial_schemas", "7s-schema.yaml"),
+ ],
+ self.f("partial_schemas", "7s-data.yaml"),
+ {
+ 'type': 'map',
+ 'mapping': {
+ 'foo': {
+ 'type': 'str',
+ 'required': True
+ },
+ 'bar': {
+ 'include': 'bar'
+ }
+ }
+ }
+ )
+ ]
+
+ failing_tests = [
+ # Test include inside partial schema
+ (
+ [
+ self.f("partial_schemas", "1f-schema.yaml"),
+ self.f("partial_schemas", "1f-partials.yaml")
+ ],
+ self.f("partial_schemas", "1f-data.yaml"),
+ SchemaError,
+ ["Cannot find partial schema with name 'fooonez'. Existing partial schemas: 'bar, fooone, foothree, footwo'. Path: '/0'"]
+ ),
+ (
+ [
+ self.f('partial_schemas', '2f-schema.yaml')
+ ],
+ self.f('partial_schemas', '2f-data.yaml'),
+ SchemaError,
+ ["Value 'True' is not of type 'str'. Path: '/0'"]
+ ),
+ (
+ [
+ self.f('partial_schemas', '3f-schema.yaml')
+ ],
+ self.f('partial_schemas', '3f-data.yaml'),
+ SchemaError,
+ ["Value 'True' is not of type 'str'. Path: ''"]
+ ),
+ (
+ [
+ self.f('partial_schemas', '4f-schema.yaml')
+ ],
+ self.f('partial_schemas', '4f-data.yaml'),
+ SchemaError,
+ ["Value 'True' is not of type 'str'. Path: '/0/foo/0/bar'"]
+ ),
+ (
+ [
+ self.f('partial_schemas', '5f-schema.yaml')
+ ],
+ self.f('partial_schemas', '5f-data.yaml'),
+ SchemaError,
+ ["Value 'True' is not of type 'str'. Path: '/0/0/0/0'"]
+ ),
+ (
+ [
+ self.f('partial_schemas', '6f-schema.yaml')
+ ],
+ self.f('partial_schemas', '6f-data.yaml'),
+ SchemaError,
+ ["Value 'True' is not of type 'str'. Path: '/foo/bar/qwe/ewq'"]
+ )
+ ]
+
+ for passing_test in pass_tests:
+ try:
+ c = Core(source_file=passing_test[1], schema_files=passing_test[0])
+ c.validate()
+ compare(c.validation_errors, [], prefix="No validation errors should exist...")
+ except Exception as e:
+ print("ERROR RUNNING FILE: {0} : {1}".format(passing_test[0], passing_test[1]))
+ raise e
+
+ # This serve as an extra schema validation that tests more complex structures then testrule.py do
+ compare(c.root_rule.schema_str, passing_test[2], prefix="Parsed rules is not correct, something have changed...")
+
+ for failing_test in failing_tests:
+ with pytest.raises(failing_test[2], message="Test files: {0} : {1}".format(", ".join(failing_test[0]), failing_test[1])):
+ c = Core(schema_files=failing_test[0], source_file=failing_test[1])
+ c.validate()
+
+ if not c.validation_errors:
+ raise AssertionError("No validation_errors was raised...")
+
+ compare(
+ sorted(c.validation_errors),
+ sorted(failing_test[3]),
+ prefix="Wrong validation errors when parsing files : {0} : {1}".format(
+ failing_test[0],
+ failing_test[1],
+ ),
+ )
+
+ def test_core_files(self):
+ # These tests should pass with no exception raised
+ pass_tests = [
+ # All tests for keyword assert
+ "test_assert.yaml",
+ # All tests for keyword default
+ "test_default.yaml",
+ # All tests for keyword desc
+ "test_desc.yaml",
+ # All tests for keyword enum
+ "test_enum.yaml",
+ # All tests for keyword example
+ "test_example.yaml",
+ # All tests for keyword extensions
+ "test_extensions.yaml",
+ # All tests for keyword func
+ "test_func.yaml",
+ # All tests for keyword ident
+ "test_ident.yaml",
+ # All tests for keyword include
+ "test_include.yaml",
+ # All tests for keyword length
+ "test_length.yaml",
+ # All tests for keyword mapping
+ "test_mapping.yaml",
+ # All tests for keyword matching
+ "test_matching.yaml",
+ # All tests for keyword name
+ "test_name.yaml",
+ # All tests for keyword nullable
+ "test_nullable.yaml",
+ # All tests for keyword pattern
+ "test_pattern.yaml",
+ # All tests for keyword range
+ "test_range.yaml",
+ # All tests for keyword required
+ "test_required.yaml",
+ # All tests for keyword schema
+ "test_schema.yaml",
+ # All tests for keyword sequence
+ "test_sequence.yaml",
+ # All tests for keyword unique
+ "test_unique.yaml",
+ # All tests for keyword version
+ "test_version.yaml",
+
+ # All test cases for Multiple sequence checks
+ "test_sequence_multi.yaml",
+ # All test cases for merging
+ "test_merge.yaml",
+ # All test cases for yaml anchors
+ "test_anchor.yaml",
+
+ # All tests for TYPE: any
+ "test_type_any.yaml",
+ # All tests for TYPE: bool
+ "test_type_bool.yaml",
+ # All tests for TYPE: date
+ "test_type_date.yaml",
+ # All tests for TYPE: enum
+ "test_type_enum.yaml",
+ # All tests for TYPE: float
+ "test_type_float.yaml",
+ # All tests for TYPE: int
+ "test_type_int.yaml",
+ # All tests for TYPE: map
+ "test_type_map.yaml",
+ # All tests for TYPE: none
+ "test_type_none.yaml",
+ # All tests for TYPE: number
+ "test_type_number.yaml",
+ # All tests for TYPE: scalar
+ "test_type_scalar.yaml",
+ # All tests for TYPE: seq
+ "test_type_seq.yaml",
+ # All tests for TYPE: str
+ "test_type_str.yaml",
+ # All tests for TYPE: symbol
+ "test_type_symbol.yaml",
+ # All tests for TYPE: text
+ "test_type_text.yaml",
+ # All tests for TYPE: timestamp
+ "test_type_timestamp.yaml",
+ ]
+
+ _fail_tests = [
+ # All tests for keyword assert
+ ("test_assert.yaml", SchemaError),
+ # All tests for keyword default
+ ("test_default.yaml", SchemaError),
+ # All tests for keyword desc
+ ("test_desc.yaml", SchemaError),
+ # All tests for keyword enum
+ ("test_enum.yaml", SchemaError),
+ # All tests for keyword example
+ ("test_example.yaml", SchemaError),
+ # All tests for keyword extensions
+ ("test_extensions.yaml", SchemaError),
+ # All tests for keyword func
+ ("test_func.yaml", SchemaError),
+ # All tests for keyword ident
+ ("test_ident.yaml", SchemaError),
+ # All tests for keyword include
+ ("test_include.yaml", SchemaError),
+ # All tests for keyword length
+ ("test_length.yaml", SchemaError),
+ # All tests for keyword mapping
+ ("test_mapping.yaml", SchemaError),
+ # All tests for keyword matching
+ ("test_matching.yaml", SchemaError),
+ # All tests for keyword name
+ ("test_name.yaml", SchemaError),
+ # All tests for keyword nullable
+ ("test_nullable.yaml", SchemaError),
+ # All tests for keyword pattern
+ ("test_pattern.yaml", SchemaError),
+ # All tests for keyword range
+ ("test_range.yaml", SchemaError),
+ # All tests for keyword required
+ ("test_required.yaml", SchemaError),
+ # All tests for keyword schema
+ ("test_schema.yaml", SchemaError),
+ # All tests for keyword sequence
+ ("test_sequence.yaml", SchemaError),
+ # All tests for keyword unique
+ ("test_unique.yaml", SchemaError),
+ # All tests for keyword version
+ ("test_version.yaml", SchemaError),
+
+ # All test cases for Multiple sequence checks
+ ("test_sequence_multi.yaml", SchemaError),
+ # All test cases for merging
+ ("test_merge.yaml", SchemaError),
+ # All test cases for yaml anchors
+ ("test_anchor.yaml", SchemaError),
+
+ # All tests for TYPE: any
+ ("test_type_any.yaml", SchemaError),
+ # All tests for TYPE: bool
+ ("test_type_bool.yaml", SchemaError),
+ # All tests for TYPE: date
+ ("test_type_date.yaml", SchemaError),
+ # All tests for TYPE: float
+ ("test_type_float.yaml", SchemaError),
+ # All tests for TYPE: int
+ ("test_type_int.yaml", SchemaError),
+ # All tests for TYPE: map
+ ("test_type_map.yaml", SchemaError),
+ # All tests for TYPE: none
+ ("test_type_none.yaml", SchemaError),
+ # All tests for TYPE: number
+ ("test_type_number.yaml", SchemaError),
+ # All tests for TYPE: scalar
+ ("test_type_scalar.yaml", SchemaError),
+ # All tests for TYPE: seq
+ ("test_type_seq.yaml", SchemaError),
+ # All tests for TYPE: str
+ ("test_type_str.yaml", SchemaError),
+ # All tests for TYPE: symbol
+ ("test_type_symbol.yaml", SchemaError),
+ # All tests for TYPE: text
+ ("test_type_text.yaml", SchemaError),
+ # All tests for TYPE: timestamp
+ ("test_type_timestamp.yaml", SchemaError),
+ ]
+
+ # Add override magic to make it easier to test a specific file
+ if "S" in os.environ:
+ pass_tests = [os.environ["S"]]
+ _fail_tests = []
+ elif "F" in os.environ:
+ pass_tests = []
+ _fail_tests = [(os.environ["F"], SchemaError)]
+
+ for passing_test_file in pass_tests:
+ f = self.f(os.path.join("success", passing_test_file))
+ with open(f, "r") as stream:
+ yaml_data = yaml.safe_load_all(stream)
+
+ for document_index, document in enumerate(yaml_data):
+ data = document["data"]
+ schema = document["schema"]
+
+ try:
+ print("Running test files: {0}".format(f))
+ c = Core(source_data=data, schema_data=schema, strict_rule_validation=True, allow_assertions=True)
+ c.validate()
+ compare(c.validation_errors, [], prefix="No validation errors should exist...")
+ except Exception as e:
+ print("ERROR RUNNING FILES: {0} : {1}:{2}".format(f, document_index, document.get('name', 'UNKNOWN')))
+ raise e
+
+ # This serve as an extra schema validation that tests more complex structures then testrule.py do
+ compare(c.root_rule.schema_str, schema, prefix="Parsed rules is not correct, something have changed... files : {0} : {1}".format(f, document_index))
+
+ for failing_test, exception_type in _fail_tests:
+ f = self.f(os.path.join("fail", failing_test))
+ with open(f, "r") as stream:
+ yaml_data = yaml.safe_load_all(stream)
+
+ for document_index, document in enumerate(yaml_data):
+ data = document["data"]
+ schema = document["schema"]
+ errors = document.get("errors", [])
+
+ try:
+ print("Running test files: {0}".format(f))
+ c = Core(source_data=data, schema_data=schema, strict_rule_validation=True, allow_assertions=True)
+ c.validate()
+ except exception_type as e:
+ pass
+ else:
+ print("ERROR RUNNING FILES: {0} : {1}:{2}".format(f, document_index, document.get('name', 'UNKNOWN')))
+ raise AssertionError("Exception {0} not raised as expected... FILES: {1} : {2} : {3}:{4}".format(
+ exception_type, exception_type, failing_test, document_index, document.get('name', 'UNKNOWN')))
+
+ compare(sorted(c.validation_errors), sorted(errors), prefix="Wrong validation errors when parsing files : {0} : {1} : {2}".format(
+ f, document_index, document.get('name', 'UNKNOWN')))
diff --git a/tests/test_core_methods.py b/tests/test_core_methods.py
new file mode 100644
index 0000000..2208b79
--- /dev/null
+++ b/tests/test_core_methods.py
@@ -0,0 +1,308 @@
+# -*- coding: utf-8 -*-
+import pytest
+from datetime import datetime
+
+from pykwalify.compat import unicode
+from pykwalify.core import Core
+# from pykwalify.errors import NotSequenceError, CoreError
+from pykwalify.errors import CoreError
+
+
+class Rule(object):
+ def __init__(self, sequence=None, mapping=None, rule_type=None):
+ self.sequence = sequence or []
+ self.mapping = mapping or {}
+ self.type = rule_type or ''
+
+
+def _remap_errors(c):
+ return [unicode(error) for error in c.errors]
+
+
+# TODO: Refactor this becuase it no longer raises NotSequenceError but it now adds an error to the
+# error stack and it should look for that one instead.
+# def test_validate_sequence():
+# # If the type is set to sequence but value is int, it should raise NotSequenceError
+# with pytest.raises(NotSequenceError):
+# c = Core(source_data={}, schema_data={})
+# c._validate_sequence(123, Rule(sequence=['']), '', [])
+
+
+def ec():
+ # Return a empty core object
+ return Core(source_data={}, schema_data={})
+
+
+def test_validate_range():
+ data_matrix = [
+ (10, 5, 10, 5, 7, []),
+ (None, None, None, None, 7, []),
+
+ (10, 5, None, None, 13, ["Type 'prefix' has size of '13', greater than max limit '10'. Path: '/'"]),
+ (10, 5, None, None, 3, ["Type 'prefix' has size of '3', less than min limit '5'. Path: '/'"]),
+ (10, 5, None, None, 13.5, ["Type 'prefix' has size of '13.5', greater than max limit '10'. Path: '/'"]),
+ (10, 5, None, None, 3.5, ["Type 'prefix' has size of '3.5', less than min limit '5'. Path: '/'"]),
+ (10, 5, None, None, 10, []),
+ (10, 5, None, None, 5, []),
+ (10, 5, None, None, 10.0, []),
+ (10, 5, None, None, 5.0, []),
+
+ (None, None, 10, 5, 13, ["Type 'prefix' has size of '13', greater than or equals to max limit(exclusive) '10'. Path: '/'"]),
+ (None, None, 10, 5, 3, ["Type 'prefix' has size of '3', less than or equals to min limit(exclusive) '5'. Path: '/'"]),
+ (None, None, 10, 5, 13.5, ["Type 'prefix' has size of '13.5', greater than or equals to max limit(exclusive) '10'. Path: '/'"]),
+ (None, None, 10, 5, 3.5, ["Type 'prefix' has size of '3.5', less than or equals to min limit(exclusive) '5'. Path: '/'"]),
+ (None, None, 10, 5, 10, ["Type 'prefix' has size of '10', greater than or equals to max limit(exclusive) '10'. Path: '/'"]),
+ (None, None, 10, 5, 5, ["Type 'prefix' has size of '5', less than or equals to min limit(exclusive) '5'. Path: '/'"]),
+ (None, None, 10, 5, 8, []),
+ (None, None, 10, 5, 7, []),
+ (None, None, 10, 5, 8.5, []),
+ (None, None, 10, 5, 7.5, []),
+ ]
+
+ for max_, min_, max_ex, min_ex, value, errors in data_matrix:
+ print(u"Testing data: {0} {1} {2} {3} {4}".format(max_, min_, max_ex, min_ex, value))
+ c = ec()
+ c._validate_range(max_, min_, max_ex, min_ex, value, '/', 'prefix')
+ assert _remap_errors(c) == errors
+
+ # Test value type validation inside the method
+ with pytest.raises(CoreError):
+ c = ec()
+ c._validate_range(5, 1, None, None, [1, 2, 3], '/', 'prefix')
+
+ with pytest.raises(CoreError):
+ c = ec()
+ c._validate_range(5, 1, None, None, {'a': 1, 'b': 2, 'c': 3}, '/', 'prefix')
+
+
+def test_validate_timestamp():
+ data_matrix = [
+ ("", ["Timestamp value is empty. Path: ''"]),
+ ("1234567", []),
+ ("2016-01-01", []),
+ ("2016-01-01 15:01", []),
+ (123, []),
+ (1.5, []),
+ (0, ["Integer value of timestamp can't be below 0"]),
+ (-1, ["Integer value of timestamp can't be below 0"]),
+ (3147483647, ["Integer value of timestamp can't be above 2147483647"]),
+ ([], ["Not a valid timestamp"]),
+ (datetime.now(), []),
+ (datetime.today(), []),
+ ]
+
+ for data in data_matrix:
+ c = ec()
+ c._validate_scalar_timestamp(data[0], '')
+ assert _remap_errors(c) == data[1]
+
+
+def test_validate_date():
+ formats = ["%Y-%m-%d"]
+
+ data_matrix = [
+ (datetime.now(), [[], []]),
+ (datetime.today(), [[], []]),
+ ("1234567", [["Not a valid date: 1234567 format: %Y-%m-%d. Path: ''"], []]),
+ ("2016-01-01", [[], []]),
+ ("2016-01-01 15:01", [["Not a valid date: 2016-01-01 15:01 format: %Y-%m-%d. Path: ''"], []]),
+ (-1, [["Not a valid date: -1 date must be a string or a datetime.date not a 'int'"], []]),
+ (0, [["Not a valid date: 0 date must be a string or a datetime.date not a 'int'"], []]),
+ (1.5, [["Not a valid date: 1.5 date must be a string or a datetime.date not a 'float'"], []]),
+ (3147483647, [["Not a valid date: 3147483647 date must be a string or a datetime.date not a 'int'"], []]),
+ ([], [["Not a valid date: [] date must be a string or a datetime.date not a 'list'"], []]),
+ ({}, [["Not a valid date: {} date must be a string or a datetime.date not a 'dict'"], []]),
+ ]
+
+ for data in data_matrix:
+ for i, format in enumerate(formats):
+ print("Validating: {0} Format: {1}".format(data[0], format))
+ c = ec()
+ c._validate_scalar_date(data[0], [format], '')
+ assert _remap_errors(c) == data[1][i]
+
+
+def test_validate_scalar_type():
+ # Test that when providing a scalar type that do not exists, it should raise an error
+ with pytest.raises(CoreError):
+ c = ec()
+ c._validate_scalar_type(True, True, '')
+
+ data_matrix = []
+
+ # Tests for str
+ data_matrix += [
+ ("", "str", []),
+ ("123", "str", []),
+ ("yes", "str", []),
+ ("no", "str", []),
+ (b"foobar", "str", []),
+ (u"Néron", "str", []),
+ (123, "str", ["Value '123' is not of type 'str'. Path: ''"]),
+ (None, "str", ["Value 'None' is not of type 'str'. Path: ''"]),
+ (3.14, "str", ["Value '3.14' is not of type 'str'. Path: ''"]),
+ (True, "str", ["Value 'True' is not of type 'str'. Path: ''"]),
+ ({'a': 'b'}, "str", ["Value '{'a': 'b'}' is not of type 'str'. Path: ''"]),
+ (['a', 'b'], "str", ["Value '['a', 'b']' is not of type 'str'. Path: ''"]),
+ ]
+
+ # Tests for int
+ data_matrix += [
+ (123, "int", []),
+ (3.14, "int", ["Value '3.14' is not of type 'int'. Path: ''"]),
+ ("", "int", ["Value '' is not of type 'int'. Path: ''"]),
+ ("123", "int", ["Value '123' is not of type 'int'. Path: ''"]),
+ # (b"foobar", "int", ["Value b'foobar' is not of type 'int'. Path: ''"]),
+ (u"Néron", "int", [u"Value 'Néron' is not of type 'int'. Path: ''"]),
+ (None, "int", ["Value 'None' is not of type 'int'. Path: ''"]),
+ (True, "int", ["Value 'True' is not of type 'int'. Path: ''"]),
+ ({'a': 'b'}, "int", ["Value '{'a': 'b'}' is not of type 'int'. Path: ''"]),
+ (['a', 'b'], "int", ["Value '['a', 'b']' is not of type 'int'. Path: ''"]),
+ ]
+
+ # Tests for float type
+ data_matrix += [
+ ("1e-06", 'float', []),
+ ("1z-06", 'float', ["Value '1z-06' is not of type 'float'. Path: ''"]),
+ (1.5, 'float', []),
+ ("abc", 'float', ["Value 'abc' is not of type 'float'. Path: ''"]),
+ # (b"abc", 'float', ["Value 'abc' is not of type 'float'. Path: ''"]),
+ (u"abc", 'float', ["Value 'abc' is not of type 'float'. Path: ''"]),
+ (True, 'float', ["Value 'True' is not of type 'float'. Path: ''"]),
+ ]
+
+ # Tests for bool
+ data_matrix += [
+ (True, "bool", []),
+ (False, "bool", []),
+ (1, "bool", ["Value '1' is not of type 'bool'. Path: ''"]),
+ (3.14, "bool", ["Value '3.14' is not of type 'bool'. Path: ''"]),
+ ("", "bool", ["Value '' is not of type 'bool'. Path: ''"]),
+ ("yes", "bool", ["Value 'yes' is not of type 'bool'. Path: ''"]),
+ ("no", "bool", ["Value 'no' is not of type 'bool'. Path: ''"]),
+ # (b"foobar", "bool", [b"Value 'foobar' is not of type 'bool'. Path: ''"]),
+ (u"Néron", "bool", [u"Value 'Néron' is not of type 'bool'. Path: ''"]),
+ ([], "bool", ["Value '[]' is not of type 'bool'. Path: ''"]),
+ ({}, "bool", ["Value '{}' is not of type 'bool'. Path: ''"]),
+ ]
+
+ # Tests for number
+ data_matrix += [
+ (1, "number", []),
+ (3.14, "number", []),
+ (True, "number", ["Value 'True' is not of type 'number'. Path: ''"]),
+ (False, "number", ["Value 'False' is not of type 'number'. Path: ''"]),
+ ("", "number", ["Value '' is not of type 'number'. Path: ''"]),
+ ("yes", "number", ["Value 'yes' is not of type 'number'. Path: ''"]),
+ ("no", "number", ["Value 'no' is not of type 'number'. Path: ''"]),
+ # (b"foobar", "number", [b"Value 'foobar' is not of type 'number'. Path: ''"]),
+ (u"Néron", "number", [u"Value 'Néron' is not of type 'number'. Path: ''"]),
+ ([], "number", ["Value '[]' is not of type 'number'. Path: ''"]),
+ ({}, "number", ["Value '{}' is not of type 'number'. Path: ''"]),
+ ]
+
+ # Tests for text
+ data_matrix += [
+ (1, "text", []),
+ (3.14, "text", []),
+ ("", "text", []),
+ ("yes", "text", []),
+ ("no", "text", []),
+ # (b"foobar", "text", []),
+ (u"Néron", "text", []),
+ (True, "text", ["Value 'True' is not of type 'text'. Path: ''"]),
+ (False, "text", ["Value 'False' is not of type 'text'. Path: ''"]),
+ ([], "text", ["Value '[]' is not of type 'text'. Path: ''"]),
+ ({}, "text", ["Value '{}' is not of type 'text'. Path: ''"]),
+ (datetime(2015, 10, 24, 10, 22, 18), "text", ["Value '2015-10-24 10:22:18' is not of type 'text'. Path: ''"]),
+ ]
+
+ # Tests for any
+ data_matrix += [
+ (1, "any", []),
+ (3.14, "any", []),
+ (True, "any", []),
+ (False, "any", []),
+ ("", "any", []),
+ ("yes", "any", []),
+ ("no", "any", []),
+ # (b"foobar", "any", []),
+ (u"Néron", "any", []),
+ ([], "any", []),
+ ({}, "any", []),
+ (datetime(2015, 10, 24, 10, 22, 18), "any", []),
+ ]
+
+ # Tests for enum
+ data_matrix += [
+ ("", "enum", []),
+ ("123", "enum", []),
+ ("yes", "enum", []),
+ ("no", "enum", []),
+ # (b"foobar", "enum", []),
+ (u"Néron", "enum", []),
+ (123, "enum", ["Value '123' is not of type 'enum'. Path: ''"]),
+ (None, "enum", ["Value 'None' is not of type 'enum'. Path: ''"]),
+ (3.14, "enum", ["Value '3.14' is not of type 'enum'. Path: ''"]),
+ (True, "enum", ["Value 'True' is not of type 'enum'. Path: ''"]),
+ ({'a': 'b'}, "enum", ["Value '{'a': 'b'}' is not of type 'enum'. Path: ''"]),
+ (['a', 'b'], "enum", ["Value '['a', 'b']' is not of type 'enum'. Path: ''"]),
+ ]
+
+ # Tests for none
+ data_matrix += [
+ ("", "none", ["Value '' is not of type 'none'. Path: ''"]),
+ ("123", "none", ["Value '123' is not of type 'none'. Path: ''"]),
+ ("yes", "none", ["Value 'yes' is not of type 'none'. Path: ''"]),
+ ("no", "none", ["Value 'no' is not of type 'none'. Path: ''"]),
+ ("None", "none", ["Value 'None' is not of type 'none'. Path: ''"]),
+ # (b"foobar", "none", [b"Value 'foobar' is not of type 'none'. Path: ''"]),
+ (u"Néron", "none", [u"Value 'Néron' is not of type 'none'. Path: ''"]),
+ (123, "none", ["Value '123' is not of type 'none'. Path: ''"]),
+ (None, "none", []),
+ (3.14, "none", ["Value '3.14' is not of type 'none'. Path: ''"]),
+ (True, "none", ["Value 'True' is not of type 'none'. Path: ''"]),
+ ({'a': 'b'}, "none", ["Value '{'a': 'b'}' is not of type 'none'. Path: ''"]),
+ (['a', 'b'], "none", ["Value '['a', 'b']' is not of type 'none'. Path: ''"]),
+
+ ]
+
+ # Tests for timestamp
+ data_matrix += [
+ ("", 'timestamp', []),
+ ("1234567", 'timestamp', []),
+ ("2016-01-01", 'timestamp', []),
+ ("2016-01-01 15:01", 'timestamp', []),
+ # (b"foobar", "timestamp", []),
+ (u"Néron", "timestamp", []),
+ (123, 'timestamp', []),
+ (1.5, 'timestamp', []),
+ (0, 'timestamp', []),
+ (-1, 'timestamp', []),
+ (3147483647, 'timestamp', []),
+ ([], 'timestamp', ["Value '[]' is not of type 'timestamp'. Path: ''"]),
+ (datetime.now(), 'timestamp', []),
+ (datetime.today(), 'timestamp', []),
+ ]
+
+ data_matrix += [
+ (datetime(2015, 10, 24, 10, 22, 18), 'scalar', []),
+ ("", "scalar", []),
+ ("2016-01-01", 'scalar', []),
+ ("2016-01-01 15:01", 'scalar', []),
+ ("123", 'scalar', []),
+ ("yes", 'scalar', []),
+ (u"Néron", 'scalar', []),
+ (None, 'scalar', ["Value 'None' is not of type 'scalar'. Path: ''"]),
+ (123, 'scalar', []),
+ (3.14, 'scalar', []),
+ (True, 'scalar', []),
+ ({'a': 'b'}, 'scalar', ["Value '{'a': 'b'}' is not of type 'scalar'. Path: ''"]),
+ (['a', 'b'], 'scalar', ["Value '['a', 'b']' is not of type 'scalar'. Path: ''"]),
+ ]
+
+ for data in data_matrix:
+ print(u"Testing data: '{0!s}', '{1!s}', '{2!s}'".format(*data))
+ c = ec()
+ c._validate_scalar_type(data[0], data[1], '')
+ assert _remap_errors(c) == data[2]
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
new file mode 100644
index 0000000..7f02aab
--- /dev/null
+++ b/tests/test_exceptions.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+# pykwalify imports
+from pykwalify import errors
+
+
+class TestCLI(object):
+
+ def test_base_exception(self):
+ # retcode=2 should be schemaerror
+ e = errors.PyKwalifyException(msg="foobar", retcode=2)
+ assert e.__repr__() == "PyKwalifyException(msg='foobar')"
+ assert e.retname == "schemaerror"
+
+ def test_create_sub_class_exceptions(self):
+ u_e = errors.UnknownError()
+ assert u_e.retcode == 1
+
+ s_e = errors.SchemaError()
+ assert s_e.retcode == 2
+
+ c_e = errors.CoreError()
+ assert c_e.retcode == 3
+
+ r_e = errors.RuleError()
+ assert r_e.retcode == 4
+
+ sc_e = errors.SchemaConflict()
+ assert sc_e.retcode == 5
diff --git a/tests/test_helper.py b/tests/test_helper.py
new file mode 100644
index 0000000..8976b5f
--- /dev/null
+++ b/tests/test_helper.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# python std lib
+import logging
+import logging.config
+
+log = logging.getLogger()
+
+
+# Set the root logger to be silent so all code that uses the python logger
+# will not print anything unless we want it to, then it should be specified
+# in each test and reseted after that test
+def _set_log_lv(level=1337, loggers=None):
+ """ If no level is set then level will be so high all logging is silenced
+ """
+ if loggers is None:
+ # If no additional loggers is specified then only apply to root logger
+ log.setLevel(level)
+ for handler in log.handlers:
+ handler.level = level
+ else:
+ # If we have other logging instances specified apply to root logger and them
+ if log not in loggers:
+ loggers.append(log)
+
+ for log_instance in loggers:
+ log_instance.setLevel(level)
+ for handler in log_instance.handlers:
+ handler.level = level
+
+
+# Initially silence all logging
+_set_log_lv()
diff --git a/tests/test_rule.py b/tests/test_rule.py
new file mode 100644
index 0000000..4b2b7c9
--- /dev/null
+++ b/tests/test_rule.py
@@ -0,0 +1,399 @@
+# -*- coding: utf-8 -*-
+
+""" Unit test for pyKwalify - Rule """
+
+# python std lib
+import unittest
+
+# 3rd party imports
+import pytest
+
+# pyKwalify imports
+import pykwalify
+from pykwalify.errors import RuleError, SchemaConflict
+from pykwalify.rule import Rule
+from pykwalify.compat import unicode
+
+
+class TestRule(unittest.TestCase):
+
+ def setUp(self):
+ pykwalify.partial_schemas = {}
+
+ def test_schema(self):
+ # Test that when using both schema; and include tag that it throw an error because schema; tags should be parsed via Core()
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"schema;str": {"type": "map", "mapping": {"foo": {"type": "str"}}}, "type": "map", "mapping": {"foo": {"include": "str"}}})
+ assert str(r.value) == "<RuleError: error code 4: Schema is only allowed on top level of schema file: Path: '/'>"
+ assert r.value.error_key == 'schema.not.toplevel'
+
+ def test_unkknown_key(self):
+ # Test that providing an unknown key raises exception
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "foobar": True})
+ assert str(r.value) == "<RuleError: error code 4: Unknown key: foobar found: Path: '/'>"
+ assert r.value.error_key == 'key.unknown'
+
+ def test_matching_rule(self):
+ # Test that exception is raised when a invalid matching rule is used
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "map", "matching-rule": "foobar", "mapping": {"regex;.+": {"type": "seq", "sequence": [{"type": "str"}]}}})
+ assert str(r.value) == "<RuleError: error code 4: Specified rule in key: foobar is not part of allowed rule set : ['any', 'all']: Path: '/'>"
+ assert r.value.error_key == 'matching_rule.not_allowed'
+
+ def test_allow_empty_map(self):
+ r = Rule(schema={"type": "map", "allowempty": True, "mapping": {"name": {"type": "str"}}})
+ assert r.allowempty_map is True
+
+ def test_type_value(self):
+ # TODO: This test is currently semi broken, partial schemas might be somewhat broken possibly
+ # # Test that when only having a schema; rule it should throw error
+ # with pytest.raises(RuleError) as r:
+ # Rule(schema={"schema;fooone": {"type": "map", "mapping": {"foo": {"type": "str"}}}})
+ # assert str(r.value) == "<RuleError: error code 4: Key 'type' not found in schema rule: Path: '/'>"
+ # assert r.value.error_key == 'type.missing'
+
+ # Test a valid rule with both "str" and "unicode" types work
+ r = Rule(schema={"type": str("str")})
+ r = Rule(schema={"type": unicode("str")})
+
+ # Test that type key must be string otherwise exception is raised
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": 1})
+ assert str(r.value) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type (found int): Path: '/'>"
+ assert r.value.error_key == 'type.not_string'
+
+ # this tests that the type key must be a string
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": 1}, parent=None)
+ assert str(r.value) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type (found int): Path: '/'>"
+ assert r.value.error_key == 'type.not_string'
+
+ def test_name_value(self):
+ with pytest.raises(RuleError) as r:
+ Rule(schema={'type': 'str', 'name': {}})
+ assert str(r.value) == "<RuleError: error code 4: Value: {} for keyword name must be a string: Path: '/'>"
+
+ def test_nullable_value(self):
+ # Test that nullable value must be bool otherwise exception is raised
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "nullable": "foobar"})
+ assert str(r.value) == "<RuleError: error code 4: Value: 'foobar' for nullable keyword must be a boolean: Path: '/'>"
+ assert r.value.error_key == 'nullable.not_bool'
+
+ def test_desc_value(self):
+ with pytest.raises(RuleError) as r:
+ Rule(schema={'type': 'str', 'desc': []})
+ assert str(r.value) == "<RuleError: error code 4: Value: [] for keyword desc must be a string: Path: '/'>"
+
+ def test_example_value(self):
+ with pytest.raises(RuleError) as r:
+ Rule(schema={'type': 'str', 'example': []})
+ assert str(r.value) == "<RuleError: error code 4: Value: [] for keyword example must be a string: Path: '/'>"
+
+ def test_required_value(self):
+ # Test that required value must be bool otherwise exception is raised
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "required": "foobar"})
+ assert str(r.value) == "<RuleError: error code 4: Value: 'foobar' for required keyword must be a boolean: Path: '/'>"
+ assert r.value.error_key == 'required.not_bool'
+
+ def test_pattern_value(self):
+ # this tests a invalid regexp pattern
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "pattern": "/@/\\"})
+ assert str(r.value) == "<RuleError: error code 4: Syntax error when compiling regex pattern: None: Path: '/'>"
+ assert r.value.error_key == 'pattern.syntax_error'
+
+ # Test that pattern keyword is not allowed when using a map
+ # with self.assertRaisesRegexp(RuleError, ".+map\.pattern.+"):
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "map", "pattern": "^[a-z]+$", "allowempty": True, "mapping": {"name": {"type": "str"}}})
+ assert str(r.value) == "<RuleError: error code 4: Keyword pattern is not allowed inside map: Path: '/'>"
+ assert r.value.error_key == 'pattern.not_allowed_in_map'
+
+ # Test that pattern value must be string otherwise exception is raised
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "pattern": 1})
+ assert str(r.value) == "<RuleError: error code 4: Value of pattern keyword: '1' is not a string: Path: '/'>"
+ assert r.value.error_key == 'pattern.not_string'
+
+ def test_date_and_format_value(self):
+ r = Rule(schema={"type": "date", "format": "%y"})
+ assert r.format is not None, "date var not set proper"
+ assert isinstance(r.format, list), "date format should be a list"
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "date", "format": 1})
+ assert str(r.value) == "<RuleError: error code 4: Value of format keyword: '1' must be a string or list or string values: Path: '/'>"
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "map", "format": "%y"})
+ assert str(r.value) == "<RuleError: error code 4: Keyword format is only allowed when used with the following types: ('date',): Path: '/'>"
+
+ def test_enum_value(self):
+ # this tests the various valid enum types
+ Rule(schema={"type": "int", "enum": [1, 2, 3]})
+ Rule(schema={"type": "bool", "enum": [True, False]})
+ r = Rule(schema={"type": "str", "enum": ["a", "b", "c"]})
+ assert r.enum is not None, "enum var is not set proper"
+ assert isinstance(r.enum, list), "enum is not set to a list"
+ assert len(r.enum) == 3, "invalid length of enum entries"
+
+ # this tests the missmatch between the type and the data inside a enum
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "enum": [1, 2, 3]})
+ assert str(r.value).startswith("<RuleError: error code 4: Item: '1' in enum is not of correct class type:")
+ assert r.value.error_key == 'enum.type.unmatch'
+
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "enum": True})
+ assert str(r.value) == "<RuleError: error code 4: Enum is not a sequence: Path: '/'>"
+ assert r.value.error_key == 'enum.not_seq'
+
+ def test_assert_value(self):
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "seq", "sequence": [{"type": "str", "assert": 1}]})
+ assert str(r.value) == "<RuleError: error code 4: Value: '1' for keyword 'assert' is not a string: Path: '/sequence/0'>"
+ assert r.value.error_key == 'assert.not_str'
+
+ # Test that invalid characters is not present
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "seq", "sequence": [{"type": "str", "assert": "__import__"}]})
+ assert str(r.value) == "<RuleError: error code 4: Value: '__import__' contain invalid content that is not allowed to be present in assertion keyword: Path: '/sequence/0'>" # NOQA: E501
+ assert r.value.error_key == 'assert.unsupported_content'
+
+ def test_length(self):
+ r = Rule(schema={"type": "int", "length": {"max": 10, "min": 1}})
+ assert r.length is not None, "length var not set proper"
+ assert isinstance(r.length, dict), "range var is not of dict type"
+
+ # this tests that the range key must be a dict
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "length": []})
+ assert str(r.value) == "<RuleError: error code 4: Length value is not a dict type: '[]': Path: '/'>"
+ assert r.value.error_key == 'length.not_map'
+
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "length": {"max": "z"}})
+ assert str(r.value) == "<RuleError: error code 4: Value: 'z' for 'max' keyword is not a number: Path: '/'>"
+ assert r.value.error_key == 'length.max.not_number'
+
+ # this tests that min is bigger then max that should not be possible
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "length": {"max": 10, "min": 11}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'max' can't be less then value for 'min'. 10 < 11: Path: '/'>"
+ assert r.value.error_key == 'length.max_lt_min'
+
+ # test that min-ex is bigger then max-ex, that should not be possible
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "length": {"max-ex": 10, "min-ex": 11}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'max-ex' can't be less then value for 'min-ex'. 10 <= 11: Path: '/'>"
+ assert r.value.error_key == 'length.max-ex_le_min-ex'
+
+ # test that a string has non negative boundaries
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "length": {"max": -1, "min": -2}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type str.: Path: '/'>"
+ assert r.value.error_key == 'length.min_negative'
+
+ # test that a seq has non negative boundaries
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "seq", "length": {"max": 3, "min": -2}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type seq.: Path: '/'>"
+ assert r.value.error_key == 'length.min_negative'
+
+ def test_range_value(self):
+ r = Rule(schema={"type": "int", "range": {"max": 10, "min": 1}})
+ assert r.range is not None, "range var not set proper"
+ assert isinstance(r.range, dict), "range var is not of dict type"
+
+ # this tests that the range key must be a dict
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "range": []})
+ assert str(r.value) == "<RuleError: error code 4: Range value is not a dict type: '[]': Path: '/'>"
+ assert r.value.error_key == 'range.not_map'
+
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "range": {"max": "z"}})
+ assert str(r.value) == "<RuleError: error code 4: Value: 'z' for 'max' keyword is not a number: Path: '/'>"
+ assert r.value.error_key == 'range.max.not_number'
+
+ # this tests that min is bigger then max that should not be possible
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "range": {"max": 10, "min": 11}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'max' can't be less then value for 'min'. 10 < 11: Path: '/'>"
+ assert r.value.error_key == 'range.max_lt_min'
+
+ # test that min-ex is bigger then max-ex, that should not be possible
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "int", "range": {"max-ex": 10, "min-ex": 11}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'max-ex' can't be less then value for 'min-ex'. 10 <= 11: Path: '/'>"
+ assert r.value.error_key == 'range.max-ex_le_min-ex'
+
+ # test that a string has non negative boundaries
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "range": {"max": -1, "min": -2}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type str.: Path: '/'>"
+ assert r.value.error_key == 'range.min_negative'
+
+ # test that a seq has non negative boundaries
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "seq", "range": {"max": 3, "min": -2}})
+ assert str(r.value) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type seq.: Path: '/'>"
+ assert r.value.error_key == 'range.min_negative'
+
+ def test_ident_value(self):
+ pass
+
+ def test_unique_value(self):
+ # this tests that this cannot be used in the root level
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "str", "unique": True})
+ assert str(r.value) == "<RuleError: error code 4: Keyword 'unique' can't be on root level of schema: Path: '/'>"
+ assert r.value.error_key == 'unique.not_on_root_level'
+
+ # this tests that unique cannot be used at root level
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "seq", "unique": True})
+ assert str(r.value) == "<RuleError: error code 4: Type of the value: 'seq' for 'unique' keyword is not a scalar type: Path: '/'>"
+ assert r.value.error_key == 'unique.not_scalar'
+
+ def test_sequence(self):
+ # this tests seq type with a internal type of str
+ r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]})
+ assert r.type is not None, "rule not contain type var"
+ assert r.type == "seq", "type not 'seq'"
+ assert r.sequence is not None, "rule not contain sequence var"
+ assert isinstance(r.sequence, list), "rule is not a list"
+
+ # Test basic sequence rule
+ r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]})
+ assert r.type == "seq"
+ assert isinstance(r.sequence, list)
+ assert isinstance(r.sequence[0], Rule)
+ assert r.sequence[0].type == "str"
+
+ # Test sequence without explicit type
+ r = Rule(schema={"sequence": [{"type": "str"}]})
+ assert r.type == "seq"
+ assert isinstance(r.sequence, list)
+ assert isinstance(r.sequence[0], Rule)
+ assert r.sequence[0].type == "str"
+
+ # Test short name 'seq'
+ r = Rule(schema={"seq": [{"type": "str"}]})
+ assert r.type == "seq"
+ assert isinstance(r.sequence, list)
+ assert isinstance(r.sequence[0], Rule)
+ assert r.sequence[0].type == "str"
+
+ # Test error is raised when sequence key is missing
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "seq"})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Type is sequence but no sequence alias found on same level: Path: '/'>"
+
+ # sequence and pattern can't be used at same time
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "seq", "sequence": [{"type": "str"}], "pattern": "..."})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Sequence and pattern can't be on the same level in the schema: Path: '/'>"
+
+ def test_build_sequence_multiple_values(self):
+ """
+ Test with multiple values.
+ """
+ # Test basic sequence rule
+ r = Rule(schema={'type': 'seq', 'sequence': [{'type': 'str'}, {'type': 'int'}]})
+ assert r.type == "seq"
+ assert r.matching == "any"
+ assert len(r.sequence) == 2
+ assert isinstance(r.sequence, list)
+ assert all(isinstance(r.sequence[i], Rule) for i in range(len(r.sequence)))
+ assert r.sequence[0].type == "str"
+ assert r.sequence[1].type == "int"
+
+ # Test sequence without explicit type
+ r = Rule(schema={'sequence': [{'type': 'str'}, {'type': 'int'}]})
+ assert r.type == "seq"
+ assert r.matching == "any"
+ assert len(r.sequence) == 2
+ assert isinstance(r.sequence, list)
+ assert all(isinstance(r.sequence[i], Rule) for i in range(len(r.sequence)))
+ assert r.sequence[0].type == "str"
+ assert r.sequence[1].type == "int"
+
+ # Test adding matchin rules
+
+ def test_mapping(self):
+ # This tests mapping with a nested type and pattern
+ r = Rule(schema={"type": "map", "mapping": {"name": {"type": "str", "pattern": ".+@.+"}}})
+ assert r.type == "map", "rule type is not map"
+ assert isinstance(r.mapping, dict), "mapping is not dict"
+ assert r.mapping["name"].type == "str", "nested mapping is not of string type"
+ assert r.mapping["name"].pattern is not None, "nested mapping has no pattern var set"
+ assert r.mapping["name"].pattern == ".+@.+", "pattern is not set to correct value"
+
+ # when type is specefied, 'mapping' key must be present
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "map"})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Type is mapping but no mapping alias found on same level: Path: '/'>"
+
+ # 'map' and 'enum' can't be used at same time
+ # TODO: This do not work because it currently raises RuleError: <RuleError: error code 4: enum.notscalar>
+ # with pytest.raises(SchemaConflict):
+ # r = Rule(schema={"type": "map", "enum": [1, 2, 3]})
+
+ # Test that 'map' and 'mapping' can't be at the same level
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"map": {"stream": {"type": "any"}}, "mapping": {"seams": {"type": "any"}}})
+ assert str(r.value) == "<RuleError: error code 4: Keywords 'map' and 'mapping' can't be used on the same level: Path: '/'>"
+ assert r.value.error_key == 'mapping.duplicate_keywords'
+
+ # This will test that a invalid regex will throw error when parsing rules
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "map", "matching-rule": "any", "mapping": {"regex;(+": {"type": "seq", "sequence": [{"type": "str"}]}}})
+ assert str(r.value) == "<RuleError: error code 4: Unable to compile regex '(+': Path: '/'>"
+ assert r.value.error_key == 'mapping.regex.compile_error'
+
+ # this tests map/dict but with no elements
+ with pytest.raises(RuleError) as r:
+ Rule(schema={"type": "map", "mapping": {}})
+ assert str(r.value) == "<RuleError: error code 4: Mapping do not contain any elements: Path: '/'>"
+ assert r.value.error_key == 'mapping.no_elements'
+
+ def test_default_value(self):
+ pass
+
+ def test_check_conflicts(self):
+ # TODO: This do not work and enum schema conflict is not raised... RuleError: <RuleError: error code 4: enum.notscalar>
+ # with pytest.raises(SchemaConflict) as ex:
+ # r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}], "enum": [1, 2, 3]})
+ # assert ex.value.msg.startswith("seq.conflict :: enum"), "Wrong exception was raised"
+
+ # Test sequence and mapping can't be used at same level
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "seq", "sequence": [{"type": "str"}], "mapping": {"name": {"type": "str", "pattern": ".+@.+"}}})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Sequence and mapping can't be on the same level in the schema: Path: '/'>"
+ assert ex.value.error_key == 'seq.conflict.mapping'
+
+ # Mapping and sequence can't used at same time
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "map", "mapping": {"foo": {"type": "str"}}, "sequence": [{"type": "str"}]})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Mapping and sequence can't be on the same level in the schema: Path: '/'>"
+ assert ex.value.error_key == 'map.conflict.sequence'
+
+ # scalar type and sequence can't be used at same time
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "int", "sequence": [{"type": "str"}]})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Scalar and sequence can't be on the same level in the schema: Path: '/'>"
+ assert ex.value.error_key == 'scalar.conflict.sequence'
+
+ # scalar type and mapping can't be used at same time
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "int", "mapping": {"foo": {"type": "str"}}})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Scalar and mapping can't be on the same level in the schema: Path: '/'>"
+ assert ex.value.error_key == 'scalar.conflict.mapping'
+
+ # scalar type and enum can't be used at same time
+ with pytest.raises(SchemaConflict) as ex:
+ Rule(schema={"type": "int", "enum": [1, 2, 3], "range": {"max": 10, "min": 1}})
+ assert str(ex.value) == "<SchemaConflict: error code 5: Enum and range can't be on the same level in the schema: Path: '/'>"
+ assert ex.value.error_key == 'enum.conflict.range'
diff --git a/tests/test_types.py b/tests/test_types.py
new file mode 100644
index 0000000..de7db87
--- /dev/null
+++ b/tests/test_types.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+""" Unit test for pyKwalify - Rule """
+
+# python std lib
+import unittest
+
+# pykwalify imports
+from pykwalify import types
+
+
+class TestTypes(unittest.TestCase):
+
+ def test_types(self):
+ """
+ Test that all type helper methods works correctly
+ """
+ assert types.type_class("str") == str
+
+ assert types.is_builtin_type("str")
+
+ assert types.is_collection_type("map")
+ assert types.is_collection_type("seq")
+ assert not types.is_collection_type("str")
+
+ assert types.is_scalar_type("str")
+ assert not types.is_scalar_type("seq")
+ assert not types.is_scalar_type("map")
+
+ assert types.is_collection([])
+ assert types.is_collection({})
+ assert not types.is_collection("foo")
+
+ assert types.is_scalar("")
+ assert types.is_scalar(True)
+ assert not types.is_scalar([])
+
+ assert types.is_correct_type("", str)
+ assert types.is_correct_type({}, dict)
+
+ assert types.is_string("foo")
+ assert not types.is_string([])
+
+ assert types.is_int(1)
+ assert not types.is_int("foo")
+
+ assert types.is_bool(True)
+ assert not types.is_bool(1)
+ assert not types.is_bool("true")
+
+ assert types.is_float(1.0)
+ assert not types.is_float("foo")
+
+ assert types.is_number(1)
+ assert types.is_number(1.0)
+ assert not types.is_number("foo")
+
+ assert types.is_text("foo")
+ assert types.is_text(1)
+ assert types.is_text(1.0)
+ assert not types.is_text([])
+ assert not types.is_text(True)
+
+ assert types.is_any("foo")
+ assert types.is_any(True)
+ assert types.is_any(1)
+ assert types.is_any(1.0)
+ assert types.is_any({})
+ assert types.is_any([])
+
+ assert types.is_enum("foo")
+ assert not types.is_enum(1)
+
+ assert types.is_none(None)
+ assert not types.is_none("foo")
diff --git a/tests/test_unicode.py b/tests/test_unicode.py
new file mode 100644
index 0000000..36f5549
--- /dev/null
+++ b/tests/test_unicode.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+
+""" Unit test for pyKwalify - Core """
+
+# python std lib
+import os
+
+# pykwalify imports
+import pykwalify
+from pykwalify.compat import unicode
+from pykwalify.core import Core
+from pykwalify.errors import SchemaError
+
+# 3rd party imports
+from pykwalify.compat import yaml
+from testfixtures import compare
+
+
+class TestUnicode(object):
+
+ def setUp(self):
+ pykwalify.partial_schemas = {}
+
+ def f(self, *args):
+ if os.path.isabs(args[0]):
+ return args[0]
+
+ return os.path.join(os.path.dirname(os.path.realpath(__file__)), "files", "unicode", *args)
+
+ def test_files_with_unicode_content_success(self, tmpdir):
+ """
+ These tests should pass with no exception raised
+ """
+ fail_data_2s_yaml = {
+ 'schema': {
+ 'type': 'map',
+ 'mapping': {
+ 'msg': {
+ 'type': 'int',
+ },
+ }
+ },
+ 'data': {
+ 'msg': 123,
+ },
+ 'errors': []
+ }
+
+ source_f = tmpdir.join(u"2så.json")
+ source_f.write(yaml.safe_dump(fail_data_2s_yaml, allow_unicode=True))
+
+ _pass_tests = [
+ # Test mapping with unicode key and value
+ u"1s.yaml",
+ # # Test unicode filename.
+ # It is not possible to package a file with unicode characters
+ # like åäö in the filename in some python versions.
+ # Mock a file with åäö during testing to properly simulate this again.
+ unicode(source_f),
+ # Test sequence with unicode keys
+ u"3s.yaml",
+ ]
+
+ for passing_test_files in _pass_tests:
+ f = self.f(passing_test_files)
+
+ with open(f, "r") as stream:
+ yaml_data = yaml.safe_load(stream)
+ data = yaml_data["data"]
+ schema = yaml_data["schema"]
+
+ try:
+ print(u"Running test files: {0}".format(f))
+ c = Core(source_data=data, schema_data=schema)
+ c.validate()
+ compare(c.validation_errors, [], prefix="No validation errors should exist...")
+ except Exception as e:
+ print(u"ERROR RUNNING FILES: {0}".format(f))
+ raise e
+
+ # This serve as an extra schema validation that tests more complex structures then testrule.py do
+ compare(c.root_rule.schema_str, schema, prefix=u"Parsed rules is not correct, something have changed... files : {0}".format(f))
+
+ def test_files_with_unicode_content_failing(self, tmpdir):
+ """
+ These tests should fail with the specified exception
+ """
+ # To trigger schema exception we must pass in a source file
+ fail_data_2f_yaml = {
+ 'schema': {
+ 'type': 'map',
+ 'mapping': {
+ 'msg': {
+ 'type': 'int',
+ },
+ }
+ },
+ 'data': {
+ 'msg': 'Foobar',
+ },
+ 'errors': ["Value 'Foobar' is not of type 'int'. Path: '/msg'"]
+ }
+
+ source_f = tmpdir.join(u"2få.json")
+ source_f.write(yaml.safe_dump(fail_data_2f_yaml, allow_unicode=True))
+
+ _fail_tests = [
+ # Test mapping with unicode key and value but wrong type
+ (u"1f.yaml", SchemaError),
+ # Test unicode filename with validation errors.
+ # It is not possible to package a file with unicode characters
+ # like åäö in the filename in some python versions.
+ # Mock a file with åäö during testing to properly simulate this again.
+ (unicode(source_f), SchemaError),
+ # Test unicode data inside seq but wrong type
+ (u"3f.yaml", SchemaError),
+ ]
+
+ for failing_test, exception_type in _fail_tests:
+ f = self.f(failing_test)
+
+ with open(f, "r") as stream:
+ yaml_data = yaml.safe_load(stream)
+ data = yaml_data["data"]
+ schema = yaml_data["schema"]
+ errors = yaml_data["errors"]
+
+ try:
+ print(u"Running test files: {0}".format(f))
+ c = Core(source_data=data, schema_data=schema)
+ c.validate()
+ except exception_type:
+ pass # OK
+ else:
+ raise AssertionError(u"Exception {0} not raised as expected... FILES: {1} : {2}".format(exception_type, exception_type))
+
+ compare(sorted(c.validation_errors), sorted(errors), prefix=u"Wrong validation errors when parsing files : {0}".format(f))