Initial commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Copyright 2026 jCloud Services GbR
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from src.jcloud_config_parser.ini import INIConfiguration, INIConfigurationSection
|
||||
from src.jcloud_config_parser.exceptions import INISyntaxError
|
||||
from src.jcloud_config_parser import set_mutability
|
||||
|
||||
def test_generating():
|
||||
config = INIConfiguration()
|
||||
config.test_group = INIConfigurationSection('test_group')
|
||||
config.test_group.key1 = 'value1'
|
||||
config.test_group.key2 = 'value2'
|
||||
assert config.to_string() == '''[test_group]
|
||||
key1=value1
|
||||
key2=value2'''
|
||||
|
||||
def test_parsing():
|
||||
# Test deserializer
|
||||
config_string = '''
|
||||
property1 = 42
|
||||
property2=123
|
||||
|
||||
[configuration_group1]
|
||||
hello=world
|
||||
key1=value1
|
||||
|
||||
[group2]
|
||||
world=hel#lo
|
||||
key2 = "val#ue2"
|
||||
[]
|
||||
hello2=world2
|
||||
invalid_line1
|
||||
|
||||
invalid_line2
|
||||
|
||||
# comment'''
|
||||
|
||||
assert dict(INIConfiguration.from_string(config_string, ignore_errors=True)) == {'configuration_group1': {'hello': 'world', 'key1': 'value1'}, 'group2': {'world': 'hel', 'key2': 'val#ue2', 'hello2': 'world2'}}
|
||||
|
||||
try:
|
||||
INIConfiguration.from_string('key="value')
|
||||
assert False, 'Expected INISyntaxError for unterminated literal'
|
||||
except INISyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
INIConfiguration.from_string('key=value"')
|
||||
assert False, 'Expected INISyntaxError for unterminated literal'
|
||||
except INISyntaxError:
|
||||
pass
|
||||
|
||||
def test_default():
|
||||
default_configuration = INIConfiguration.from_string('''[section1]
|
||||
k1=v1
|
||||
k2=v2
|
||||
k3=v3
|
||||
|
||||
[section2]
|
||||
l1=w1
|
||||
l2=w2''')
|
||||
assert dict(INIConfiguration.from_string('''[section1]
|
||||
k1=v1
|
||||
k2=v2
|
||||
k4=v4''', default = default_configuration)) == {'section1': {'k1': 'v1', 'k2': 'v2', 'k3': 'v3', 'k4': 'v4'}, 'section2': {'l1': 'w1', 'l2': 'w2'}}
|
||||
|
||||
def test_mutability():
|
||||
config = INIConfiguration(mutable=False)
|
||||
try:
|
||||
config.key = 'value'
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
set_mutability(config, True)
|
||||
assert config._mutable == True
|
||||
|
||||
config.key = 'value'
|
||||
|
||||
set_mutability(config, False)
|
||||
assert config._mutable == False
|
||||
|
||||
try:
|
||||
del config.key
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"level_1": {
|
||||
"meta": {
|
||||
"version": "1.0",
|
||||
"generated": true,
|
||||
"tags": ["test", "deep", "nested", "json"]
|
||||
},
|
||||
"level_2": {
|
||||
"array": [
|
||||
{
|
||||
"id": 1,
|
||||
"level_3": {
|
||||
"level_4": {
|
||||
"level_5": {
|
||||
"config": {
|
||||
"enabled": true,
|
||||
"thresholds": {
|
||||
"low": 0.1,
|
||||
"medium": 0.5,
|
||||
"high": 0.9
|
||||
},
|
||||
"modes": [
|
||||
{
|
||||
"name": "alpha",
|
||||
"params": {
|
||||
"retry": 3,
|
||||
"timeout": {
|
||||
"connect": 1000,
|
||||
"read": 5000,
|
||||
"deep": {
|
||||
"even_deeper": {
|
||||
"flag": false,
|
||||
"notes": [
|
||||
"still",
|
||||
"going",
|
||||
{
|
||||
"deeper": {
|
||||
"than": {
|
||||
"most": {
|
||||
"humans": {
|
||||
"expect": {
|
||||
"value": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"level_2_object": {
|
||||
"a": {
|
||||
"b": {
|
||||
"c": {
|
||||
"d": {
|
||||
"e": {
|
||||
"f": {
|
||||
"g": {
|
||||
"h": {
|
||||
"i": {
|
||||
"j": {
|
||||
"k": "bottom"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"level3": {
|
||||
"root": [
|
||||
[
|
||||
[
|
||||
[
|
||||
{
|
||||
"a": [
|
||||
{
|
||||
"b": [
|
||||
{
|
||||
"c": [
|
||||
{
|
||||
"d": [
|
||||
{ "e": [
|
||||
{ "f": "bottom"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
, "4": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"items": [
|
||||
{"x": {"y": {"z": [1,2,3,4,5]}}},
|
||||
{"x": {"y": {"z": [1,2,3,4,5]}}},
|
||||
{"x": {"y": {"z": [1,2,3,4,5]}}},
|
||||
{"x": {"y": {"z": [1,2,3,4,5]}}},
|
||||
{"x": {"y": {"z": [1,2,3,4,5]}}}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
"0":{
|
||||
"n0": {
|
||||
"n1": {
|
||||
"n2": {
|
||||
"n3": {
|
||||
"n4": {
|
||||
"n5": {
|
||||
"n6": { "n7": { "n8": { "n9": { "n10": { "n11": { "n12": {
|
||||
"n13": {
|
||||
"n14": {
|
||||
"n15": {
|
||||
"n16": {
|
||||
"n17": {
|
||||
"n18": { "n19": { "n20": "bottom"
|
||||
} } }
|
||||
}
|
||||
} }}
|
||||
}
|
||||
}
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
# Copyright 2026 jCloud Services GbR
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from src.jcloud_config_parser.json import JSONConfiguration
|
||||
from src.jcloud_config_parser.exceptions import JSONObjectSyntaxError, JSONValueSyntaxError, JSONStringSyntaxError, JSONArraySyntaxError, EscapeSequenceSyntaxError
|
||||
from src.jcloud_config_parser import set_mutability
|
||||
|
||||
def test_json_configuration():
|
||||
# Test valid JSON configuration parsing
|
||||
assert dict(JSONConfiguration.from_string('{"key1": "value1", "key2": 42, "key3": true, "key4": {"k1": 42, "k2": [1, null, true]}}')) == {"key1": "value1", "key2": 42, "key3": True, "key4": {"k1": 42, "k2": [1, None, True]}}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"name": "John Doe",
|
||||
"active": true,
|
||||
"score": 99.5,
|
||||
"roles": ["admin", "developer"],
|
||||
"meta": null
|
||||
}''')) == {'name': 'John Doe', 'active': True, 'score': 99.5, 'roles': ['admin', 'developer'], 'meta': None}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"items": [],
|
||||
"config": {},
|
||||
"enabled": false
|
||||
}''')) == {'items': [], 'config': {}, 'enabled': False}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"int": 1,
|
||||
"float": 1.0,
|
||||
"scientific": 1e3,
|
||||
"negative": -42
|
||||
}''')) == {
|
||||
'int': 1,
|
||||
'float': 1.0,
|
||||
'scientific': 1000.0,
|
||||
'negative': -42
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"text": "🌍",
|
||||
"escaped": "Line1\\nLine2\\tTabbed",
|
||||
"unicode_escape": "\u263A"
|
||||
}''')) == {
|
||||
'text': '🌍',
|
||||
'escaped': 'Line1\nLine2\tTabbed',
|
||||
'unicode_escape': '☺'
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{"m": [
|
||||
{ "id": 1, "value": "a" },
|
||||
{ "id": 2, "value": "b" },
|
||||
{ "id": 3, "value": null }
|
||||
]}
|
||||
''')) == {
|
||||
'm': [
|
||||
{'id': 1, 'value': 'a'},
|
||||
{'id': 2, 'value': 'b'},
|
||||
{'id': 3, 'value': None},
|
||||
]
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"1": "one",
|
||||
"true": "yes",
|
||||
"null": "nothing"
|
||||
}
|
||||
''')) == {
|
||||
'1': 'one',
|
||||
'true': 'yes',
|
||||
'null': 'nothing'
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"a": {
|
||||
"b": {
|
||||
"c": {
|
||||
"d": [1, 2, {"e": false}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''')) == {
|
||||
'a': {
|
||||
'b': {
|
||||
'c': {
|
||||
'd': [1, 2, {'e': False}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"x": 1,
|
||||
"x": 2,
|
||||
"x": 3
|
||||
}
|
||||
''')) == {'x': 3}
|
||||
assert dict(JSONConfiguration.from_string('''{"nested": [[[[[[[[[[[[[[[[[[[[0]]]]]]]]]]]]]]]]]]]]}''')) == {'nested': [[[[[[[[[[[[[[[[[[[[0]]]]]]]]]]]]]]]]]]]]}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"id": 9007199254740993
|
||||
}
|
||||
''')) == {'id': 9007199254740993}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"value": 0.1
|
||||
}''')) == {'value': 0.1}
|
||||
assert dict(JSONConfiguration.from_string('{ "x": null }')) == {'x': None}
|
||||
assert dict(JSONConfiguration.from_string('{}')) == {}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"a": "",
|
||||
"b": null
|
||||
}
|
||||
|
||||
''')) == {'a': '', 'b': None}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"char1": "é",
|
||||
"char2": "e\u0301"
|
||||
}''')) == {
|
||||
'char1': 'é',
|
||||
'char2': 'é'
|
||||
}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"text": "hello\u0000world"
|
||||
}''')) == {'text': 'hello\x00world'}
|
||||
assert dict(JSONConfiguration.from_string('''{
|
||||
"enabled": "false"
|
||||
}
|
||||
''')) == {'enabled': 'false'}
|
||||
assert dict(JSONConfiguration.from_string('''{"array": [1, "1", true, null, {}, []]
|
||||
}''')) == {'array': [1, '1', True, None, {}, []]}
|
||||
assert dict(JSONConfiguration.from_string('''{ "a" :
|
||||
[ 1 ,2
|
||||
,3 ] }
|
||||
|
||||
''')) == {'a': [1, 2, 3]}
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONConfiguration.from_string('{"a": 1,}')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('''{
|
||||
// Comment
|
||||
"a": 1
|
||||
}''')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"a": 1 /* Comment */}')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{a: 1}')
|
||||
assert False, 'Excepted JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{\'a\': \'b\'}')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"x": 012}')
|
||||
assert False, 'Excepted JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"x": NaN, "y": Infinity}')
|
||||
assert False, 'Excepted JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"text": "hello\nworld"}') # This is not a JSON escape sequence!
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"x": "\q"}')
|
||||
assert False, 'Excepted EscapeSequenceSyntaxError'
|
||||
except EscapeSequenceSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"x": "\\u123"}')
|
||||
assert False, 'Excepted EscapeSequenceSyntaxError'
|
||||
except EscapeSequenceSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{ "a": 1 } { "b": 2 }')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"a": 1 "b": 2}')
|
||||
assert False, 'Excepted JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('{"a": [1, 2, 3}')
|
||||
assert False, 'Excepted JSONArraySyntaxError'
|
||||
except JSONArraySyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONConfiguration.from_string('')
|
||||
assert False, 'Excepted JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
def test_mutability():
|
||||
config = JSONConfiguration(mutable=False)
|
||||
try:
|
||||
config.key = 'value'
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
set_mutability(config, True)
|
||||
assert config._mutable == True
|
||||
|
||||
config.key = 'value'
|
||||
|
||||
set_mutability(config, False)
|
||||
assert config._mutable == False
|
||||
|
||||
try:
|
||||
del config.key
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
@@ -0,0 +1,443 @@
|
||||
# Copyright 2026 jCloud Services GbR
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from src.jcloud_config_parser.parse.json import parse_escape_sequences, JSONNumber, JSONString, JSONBoolean, JSONNull, JSONArray, JSONObject, parse_type, parse_json
|
||||
from src.jcloud_config_parser.exceptions import EscapeSequenceSyntaxError, JSONNumberSyntaxError, JSONStringSyntaxError, JSONBooleanSyntaxError, JSONNullSyntaxError, JSONArraySyntaxError, JSONObjectSyntaxError, JSONValueSyntaxError
|
||||
import os
|
||||
|
||||
def test_parse_escape_sequences():
|
||||
# Test valid escape sequences
|
||||
assert parse_escape_sequences(r'Hello\nWorld') == 'Hello\nWorld'
|
||||
assert parse_escape_sequences(r'Hello\tWorld') == 'Hello\tWorld'
|
||||
assert parse_escape_sequences(r'Hello\\World') == 'Hello\\World'
|
||||
assert parse_escape_sequences(r'Hello\"World\"') == 'Hello"World"'
|
||||
assert parse_escape_sequences(r'Hello\/World') == 'Hello/World'
|
||||
assert parse_escape_sequences(r'Hello\bWorld') == 'Hello\bWorld'
|
||||
assert parse_escape_sequences(r'Hello\fWorld') == 'Hello\fWorld'
|
||||
assert parse_escape_sequences(r'Hello\rWorld') == 'Hello\rWorld'
|
||||
assert parse_escape_sequences(r'Hello\u0041World') == 'HelloAWorld'
|
||||
|
||||
# Test invalid escape sequences
|
||||
try:
|
||||
parse_escape_sequences(r'Hello\qWorld')
|
||||
assert False, 'Expected EscapeSequenceSyntaxError'
|
||||
except EscapeSequenceSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_escape_sequences(r'Hello\u00G1World')
|
||||
assert False, 'Expected EscapeSequenceSyntaxError'
|
||||
except EscapeSequenceSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_escape_sequences(r'\u123')
|
||||
assert False, 'Expected EscapeSequenceSyntaxError'
|
||||
except EscapeSequenceSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_number():
|
||||
# Test parsing integers
|
||||
assert JSONNumber.parse('0').value == 0
|
||||
assert JSONNumber.parse('42').value == 42
|
||||
assert JSONNumber.parse('-42').value == -42
|
||||
|
||||
# Test parsing floats
|
||||
assert JSONNumber.parse('0.0').value == 0.0
|
||||
assert JSONNumber.parse('3.14').value == 3.14
|
||||
assert JSONNumber.parse('-3.14').value == -3.14
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONNumber.parse('abc')
|
||||
assert False, 'Expected JSONNumberSyntaxError'
|
||||
except JSONNumberSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONNumber.parse('01')
|
||||
assert False, 'Expected JSONNumberSyntaxError'
|
||||
except JSONNumberSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_str():
|
||||
# Test parsing strings
|
||||
assert JSONString.parse('"Hello, World!"').value == 'Hello, World!'
|
||||
assert JSONString.parse(r'"Hello,\nWorld!"').value == 'Hello,\nWorld!'
|
||||
assert JSONString.parse(r'"Hello,\\World!"').value == 'Hello,\\World!'
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONString.parse('\'Hello, World!\'')
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONString.parse('"Hello, World!')
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONString.parse('Hello, World!"')
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONString.parse('Hello, World!')
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONString.parse('""Hello, World!"')
|
||||
assert False, 'Excepted JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_boolean():
|
||||
# Test parsing booleans
|
||||
assert JSONBoolean.parse('true').value == True
|
||||
assert JSONBoolean.parse('false').value == False
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONBoolean.parse('True')
|
||||
assert False, 'Expected JSONBooleanSyntaxError'
|
||||
except JSONBooleanSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONBoolean.parse('False')
|
||||
assert False, 'Expected JSONBooleanSyntaxError'
|
||||
except JSONBooleanSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONBoolean.parse('')
|
||||
assert False, 'Expected JSONBooleanSyntaxError'
|
||||
except JSONBooleanSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_null():
|
||||
# Test parsing null
|
||||
assert JSONNull.parse('null').value is None
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONNull.parse('NULL')
|
||||
assert False, 'Expected JSONNullSyntaxError'
|
||||
except JSONNullSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONNull.parse('Null')
|
||||
assert False, 'Expected JSONNullSyntaxError'
|
||||
except JSONNullSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONNull.parse('')
|
||||
assert False, 'Expected JSONNullSyntaxError'
|
||||
except JSONNullSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_array():
|
||||
# Test parsing arrays
|
||||
assert JSONArray.parse('[]').value == []
|
||||
assert JSONArray.parse('[1, 2, 3]').value == [1, 2, 3]
|
||||
assert JSONArray.parse('[1, "2", true, null]').value == [1, '2', True, None]
|
||||
assert JSONArray.parse(r'[1, "2", true, null]').value == [1, '2', True, None]
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
JSONArray.parse('[')
|
||||
assert False, 'Expected JSONArraySyntaxError'
|
||||
except JSONArraySyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
JSONArray.parse('[1, 2')
|
||||
assert False, 'Expected JSONArraySyntaxError'
|
||||
except JSONArraySyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_object():
|
||||
# Test parsing objects
|
||||
assert JSONObject.parse('{}').value == {}
|
||||
assert JSONObject.parse('{"key": "value"}').value == {'key': 'value'}
|
||||
assert JSONObject.parse('{"key1": 1, "key2": "value2", "key3": true, "key4": null}').value == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
|
||||
assert JSONObject.parse(r'{"key1": 1, "key2": "value2", "key3": true, "key4": null}').value == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
|
||||
|
||||
with open(os.path.dirname(__file__) + '/test.json') as jsonf:
|
||||
assert JSONObject.parse(jsonf.read()).value == {
|
||||
"level_1": {
|
||||
"meta": {
|
||||
"version": "1.0",
|
||||
"generated": True,
|
||||
"tags": ["test", "deep", "nested", "json"],
|
||||
},
|
||||
"level_2": {
|
||||
"array": [
|
||||
{
|
||||
"id": 1,
|
||||
"level_3": {
|
||||
"level_4": {
|
||||
"level_5": {
|
||||
"config": {
|
||||
"enabled": True,
|
||||
"thresholds": {
|
||||
"low": 0.1,
|
||||
"medium": 0.5,
|
||||
"high": 0.9,
|
||||
},
|
||||
"modes": [
|
||||
{
|
||||
"name": "alpha",
|
||||
"params": {
|
||||
"retry": 3,
|
||||
"timeout": {
|
||||
"connect": 1000,
|
||||
"read": 5000,
|
||||
"deep": {
|
||||
"even_deeper": {
|
||||
"flag": False,
|
||||
"notes": [
|
||||
"still",
|
||||
"going",
|
||||
{
|
||||
"deeper": {
|
||||
"than": {
|
||||
"most": {
|
||||
"humans": {
|
||||
"expect": {
|
||||
"value": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
],
|
||||
"level_2_object": {
|
||||
"a": {
|
||||
"b": {
|
||||
"c": {
|
||||
"d": {
|
||||
"e": {
|
||||
"f": {
|
||||
"g": {
|
||||
"h": {
|
||||
"i": {
|
||||
"j": {
|
||||
"k": "bottom"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"level3": {
|
||||
"root": [
|
||||
[
|
||||
[
|
||||
[
|
||||
{
|
||||
"a": [
|
||||
{
|
||||
"b": [
|
||||
{
|
||||
"c": [
|
||||
{
|
||||
"d": [
|
||||
{
|
||||
"e": [
|
||||
{
|
||||
"f": "bottom"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
},
|
||||
"4": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"level": {
|
||||
"items": [
|
||||
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
|
||||
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
|
||||
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
|
||||
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
|
||||
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0": {
|
||||
"n0": {
|
||||
"n1": {
|
||||
"n2": {
|
||||
"n3": {
|
||||
"n4": {
|
||||
"n5": {
|
||||
"n6": {
|
||||
"n7": {
|
||||
"n8": {
|
||||
"n9": {
|
||||
"n10": {
|
||||
"n11": {
|
||||
"n12": {
|
||||
"n13": {
|
||||
"n14": {
|
||||
"n15": {
|
||||
"n16": {
|
||||
"n17": {
|
||||
"n18": {
|
||||
"n19": {
|
||||
"n20": "bottom"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
jsonf.close()
|
||||
|
||||
# Test exceptions
|
||||
|
||||
def test_parse_type():
|
||||
# Test detecting types
|
||||
assert parse_type('42') == JSONNumber
|
||||
assert parse_type('"abc"') == JSONString
|
||||
assert parse_type('true') == JSONBoolean
|
||||
assert parse_type('false') == JSONBoolean
|
||||
assert parse_type('null') == JSONNull
|
||||
assert parse_type('[1, 2, 3, "4", "5", "6", true, false, null]') == JSONArray
|
||||
assert parse_type('{"1": 2}') == JSONObject
|
||||
assert parse_type('{"1": \n\t 2 }') == JSONObject # test whitespaces
|
||||
|
||||
# Test very large data
|
||||
with open(os.path.dirname(__file__) + '/test.json', 'r') as jsonf:
|
||||
assert parse_type('[' + jsonf.read() + ']') == JSONArray
|
||||
jsonf.close()
|
||||
|
||||
# Test exceptions
|
||||
try:
|
||||
parse_type('abc')
|
||||
assert False, 'Expected JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('1s')
|
||||
assert False, 'Expected JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('[')
|
||||
assert False, 'Expected JSONArraySyntaxError'
|
||||
except JSONArraySyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('[a b]')
|
||||
assert False, 'Expected JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('"abc"abc')
|
||||
assert False, 'Expected JSONStringSyntaxError'
|
||||
except JSONStringSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('\'abc\'abc')
|
||||
assert False, 'Expected JSONValueSyntaxError'
|
||||
except JSONValueSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('{1: 2}')
|
||||
assert False, 'Expected JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
try:
|
||||
parse_type('{"1"}')
|
||||
assert False, 'Expected JSONObjectSyntaxError'
|
||||
except JSONObjectSyntaxError:
|
||||
pass
|
||||
|
||||
def test_parse_json():
|
||||
# Test parsing JSON
|
||||
assert parse_json('{"key": "value"}') == {'key': 'value'}
|
||||
assert parse_json('[1, 2, 3]') == [1, 2, 3]
|
||||
assert parse_json('{"key1": 1, "key2": "value2", "key3": true, "key4": null}') == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
|
||||
assert parse_json(r'{"key1": 1, "key2": "value2", "key3": true, "key4": null}') == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
|
||||
@@ -0,0 +1,65 @@
|
||||
# Copyright 2026 jCloud Services GbR
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from src.jcloud_config_parser.serialize.json import serialize as serialize_json
|
||||
|
||||
def test_serialize_json():
|
||||
assert serialize_json(True) == 'true'
|
||||
assert serialize_json(False) == 'false'
|
||||
assert serialize_json(True, indent = 4) == 'true'
|
||||
|
||||
assert serialize_json(None) == 'null'
|
||||
|
||||
assert serialize_json({1: {None: True}}, indent = 4) == '''{
|
||||
"1": {
|
||||
"null": true
|
||||
}
|
||||
}'''
|
||||
assert serialize_json({1: {None: True}}) == '{"1": {"null": true}}'
|
||||
assert serialize_json({1: {None: True}, 2: 'Hello, World!'}, separators = (',', ':')) == '{"1":{"null":true},"2":"Hello, World!"}'
|
||||
|
||||
array = [True, False, 1, None, 'Hello, World!']
|
||||
assert serialize_json(array) == '[true, false, 1, null, "Hello, World!"]'
|
||||
assert serialize_json(array, 4, 'X', (';', ':')) == '''[
|
||||
XXXXtrue;
|
||||
XXXXfalse;
|
||||
XXXX1;
|
||||
XXXXnull;
|
||||
XXXX"Hello, World!"
|
||||
]'''
|
||||
assert serialize_json((1, 2, 3)) == '[1, 2, 3]'
|
||||
assert serialize_json(range(3)) == '[0, 1, 2]'
|
||||
assert serialize_json(b'\x0142') == '[1, 52, 50]'
|
||||
|
||||
assert serialize_json('Hello, World!') == '"Hello, World!"'
|
||||
assert serialize_json('Hello,\nWorld!') == '"Hello,\\nWorld!"'
|
||||
assert serialize_json('Hello,"World!') == '"Hello,\\"World!"'
|
||||
assert serialize_json('Hello,\\World!') == '"Hello,\\\\World!"'
|
||||
assert serialize_json('Hello,\bWorld!') == '"Hello,\\bWorld!"'
|
||||
assert serialize_json('Hello,\tWorld!') == '"Hello,\\tWorld!"'
|
||||
assert serialize_json('Hello,\rWorld!') == '"Hello,\\rWorld!"'
|
||||
assert serialize_json('Hello,\fWorld!') == '"Hello,\\fWorld!"'
|
||||
|
||||
assert serialize_json(0) == '0'
|
||||
assert serialize_json(1) == '1'
|
||||
assert serialize_json(-1) == '-1'
|
||||
assert serialize_json(0.0) == '0.0'
|
||||
assert serialize_json(-0.0) == '-0.0'
|
||||
assert serialize_json(-42.0) == '-42.0'
|
||||
|
||||
try:
|
||||
serialize_json({1, 2, 3})
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
@@ -0,0 +1,112 @@
|
||||
# Copyright 2026 jCloud Services GbR
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from src.jcloud_config_parser import Configuration, set_mutability, is_mutable
|
||||
|
||||
def test_crud_configuration_attrs():
|
||||
config = Configuration()
|
||||
|
||||
# Test setting an attribute
|
||||
config.abc = 42
|
||||
|
||||
# Test getting an attribute
|
||||
assert config.abc == 42
|
||||
|
||||
# Test getting a non-existing attribute
|
||||
try:
|
||||
config.non_existing
|
||||
assert False, "AttributeError was not raised"
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Test updating an attribute
|
||||
config.abc = 100
|
||||
|
||||
# Test getting the updated attribute
|
||||
assert config.abc == 100
|
||||
|
||||
# Test getting dictionary
|
||||
assert dict(config) == {'abc': 100}
|
||||
|
||||
# Test deleting an attribute
|
||||
del config.abc
|
||||
try:
|
||||
config.abc
|
||||
assert False, "AttributeError was not raised"
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def test_crud_configuration_config_items():
|
||||
config = Configuration()
|
||||
|
||||
# Test setting an item
|
||||
config['key1'] = 'value1'
|
||||
|
||||
# Test getting an item
|
||||
assert config['key1'] == 'value1'
|
||||
assert config.key1 == 'value1'
|
||||
|
||||
# Test updating an item
|
||||
config['key1'] = 'value2'
|
||||
|
||||
# Test getting the updated item
|
||||
assert config['key1'] == 'value2'
|
||||
assert config.key1 == 'value2'
|
||||
|
||||
# Test deleting an item and exceptions
|
||||
del config['key1']
|
||||
try:
|
||||
config['key1']
|
||||
assert False, "KeyError was not raised"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
config.key1
|
||||
assert False, "AttributeError was not raised"
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def test_mutability():
|
||||
config = Configuration(mutable=False)
|
||||
try:
|
||||
config.key = 'value'
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
set_mutability(config, True)
|
||||
assert config._mutable == True
|
||||
|
||||
config.key = 'value'
|
||||
|
||||
set_mutability(config, False)
|
||||
assert config._mutable == False
|
||||
assert is_mutable(config) == False
|
||||
|
||||
try:
|
||||
del config.key
|
||||
assert False, 'Expected TypeError'
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
# Test operate_on_original_object parameter of set_mutability
|
||||
config = Configuration(mutable = True)
|
||||
set_mutability(config, False, operate_on_original_object = True)
|
||||
assert is_mutable(config) == False
|
||||
|
||||
new_config = set_mutability(config, True, operate_on_original_object = False)
|
||||
assert is_mutable(new_config) == True
|
||||
assert is_mutable(config) == False
|
||||
Reference in New Issue
Block a user