def test_shape_array_functions(self):
        schema = {
            "type": "object",
            "properties": {
                "list": {
                    "type": "array"
                }
            }
        }

        data = {
            "lIst": [
                {
                    "numbeR": 1
                },
                {
                    "numbeR": 2
                }
            ]
        }

        s1 = Shape(schema, data)
        self.assertEqual(1, s1.get_prop("list.$0.number"))
        self.assertEqual(2, s1.get_prop("list.$length"))
        self.assertEqual(1, s1.get_prop("list.$1.$index"))
        self.assertEqual(data["lIst"][1], s1.get_prop("list.$1").get_data())
        with self.assertRaises(KeyError): self.assertDictEqual(1, s1.get_prop("list.0.number"))
    def test_shape_with_allocation(self):
        schema = {
            "type": "object",
            "properties": {
                "obj1": {
                    "type": "object",
                    "properties": {
                        "list": {
                            "type": "array"
                        },
                        "obj2": {
                            "type": "object",
                            "properties": {
                                "obj3": {
                                    "type": "object"
                                },
                                "obj4": {
                                    "properties": {

                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        s1 = Shape(schema=schema)
        self.assertEqual(Shape, type(s1.get_prop("obj1")))
        self.assertEqual(Shape, type(s1.get_prop("obj1.list")))
        self.assertEqual(Shape, type(s1.get_prop("obj1.obj2.obj3")))
        self.assertEqual(None, s1.get_prop("obj1.obj2.obj4"))
 def test_shape_with_extras(self):
     extras = {
         "$test": Shape(data={"number": 1})
     }
     s1 = Shape(extras=extras)
     self.assertEqual(extras["$test"], s1.get_prop("$test"))
     self.assertEqual(1, s1.get_prop("$test.number"))
    def test_shape_with_default_value(self):
        schema = {
            "type": "object",
            "properties": {
                "number": {
                    "default": 1
                }
            }
        }
        s1 = Shape(schema=schema)
        self.assertEqual(1, s1.get_prop("number"))

        data = {
            "number": 2
        }
        s1 = Shape(schema=schema, data=data)
        self.assertEqual(2, s1.get_prop("number"))
    def test_shape_no_access_to_non_defined_properties(self):
        schema = {
            "type": "object",
            "properties": {
                "obj": {
                    "type": "object"
                }
            }
        }

        data = {
            "Number": 1,
            "obj": {
                "nuMber": 1
            },
            "obj1": {
                "numBer": 1
            },
            "array1": [1, 2]
        }

        s1 = Shape(schema, data)
        self.assertDictEqual({"numBer": 1}, s1.get_prop("obj1"))
        with self.assertRaises(KeyError): self.assertDictEqual({"numBer": 1}, s1.get_prop("obj1.number"))
    def test_shape_with_allocation1(self):
        schema = {
            "type": "object",
            "properties": {
                "obj1": {
                    "type": "object"
                },
                "obj2": {
                    # TODO: validation require to check type property in schema definition,
                    # can be implemented with json schema validation
                    "properties": {
                        "obj3": {
                            "type": "object"
                        }
                    }
                }
            }
        }

        data = {
            "obj1": {
                "number": 1
            },
            "obj2": {
                "number": 1,
                "obj3": {
                    "number": 1
                }
            }
        }

        s1 = Shape(schema=schema, data=data)
        self.assertEqual(Shape, type(s1.get_prop("obj1")))
        self.assertEqual(1, s1.get_prop("obj1.number"))
        self.assertEqual(dict, type(s1.get_prop("obj2")))
        with self.assertRaises(KeyError): self.assertEqual(None, type(s1.get_prop("obj2.obj3")))
    def test_shape_lower_case_keys_access(self):
        schema = {
            "type": "object",
            "properties": {
                "obj": {
                    "type": "object"
                },
                "list": {
                    "type": "array"
                }
            }
        }

        data = {
            "Number": 1,
            "obj": {
                "nuMber": 1
            },
            "lIst": [
                {
                    "numbeR": 1
                },
                {
                    "numbeR": 2
                }
            ],
            "obj1": {
                "numBer": 1
            },
            "array1": [1, 2]
        }

        s1 = Shape(schema=schema, data=data)
        self.assertEqual(1, s1.get_prop("number"))
        self.assertEqual(1, s1.get_prop("obj.number"))
        self.assertDictEqual({"numBer": 1}, s1.get_prop("obj1"))

        self.assertEqual(1, s1.get_prop("list.$0.number"))

        with self.assertRaises(KeyError): self.assertDictEqual(1, s1.get_prop("list.0"))
        with self.assertRaises(KeyError): self.assertDictEqual(1, s1.get_prop("list.0.number"))

        s1 = Shape(data=data)
        self.assertEqual(1, s1.get_prop("number"))
    def test_shape_with_set_property(self):
        schema = {
            "type": "object",
            "properties": {
                "obj1": {
                    "type": "object",
                    "properties": {
                        "number": {
                            "format": "integer"
                        },
                        "obj2": {
                            "type": "object",
                            "properties": {
                                "number": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                },
                "list": {
                    "type": "array",
                    "properties": {
                        "number": {
                            "type": "integer"
                        }
                    }
                }
            }
        }

        data = {
            "obj1": {
                "number": 1
            },
            "list": [
                {
                    "number": 1
                }
            ]
        }

        s1 = Shape(schema=schema, data=data)

        s1.set_prop("obj1.number", 1)
        self.assertEqual(1, s1.get_prop("obj1.number"))

        s1.set_prop("obj1.obj2.number", 1)
        self.assertEqual(1, s1.get_prop("obj1.obj2.number"))

        s1.set_prop("obj1.obj2.number", "1")
        self.assertEqual(1, s1.get_prop("obj1.obj2.number"))

        with self.assertRaises(ValueError): s1.set_prop("obj1.obj2.number", "a")

        s1.set_prop("list.$0.number", 1)
        self.assertEqual(1, s1.get_prop("list.$0.number"))

        s1.set_prop("list.$0.number", "1")
        self.assertEqual(1, s1.get_prop("list.$0.number"))
        with self.assertRaises(ValueError): s1.set_prop("list.$0.number", "a")

        with self.assertRaises(IndexError): s1.set_prop("list.$1.number", "1")
        #self.assertEqual(1, s1.get_prop("list.$1.number"))

        s1.set_prop("list.$0.Number", 2)
        self.assertEqual(2, s1.get_prop("list.$0.number"))

        s1.set_prop("obj1.number", None)
        self.assertEqual(None, s1.get_prop("obj1.number"))
    def test_shape_with_parent(self):
        with self.assertRaises(TypeError): self.assertDictEqual(Shape(parent_shape={}))

        parent = Shape()
        s1 = Shape(parent_shape=parent)
        self.assertEqual(parent, s1.get_prop("$parent"))