def test_array_delete(self):
        doc0 =  Document()
        doc0.snapshot = []
        doc1 = self.doc1
        doc2 = self.doc2

        # can technically delete nothing from empty list. why not
        op1 = Op('ad', [], offset=0, val=0)
        doc0.apply_op(op1)
        self.assertEqual(doc0.snapshot, [])

        # remove one from list
        op2 = Op('ad', [], offset=1, val=1)
        doc2.apply_op(op2)
        self.assertEqual(doc2.get_value([1]), 'normal, ol string')

        # from nested lists
        op3 = Op('ad', [2], offset=1, val=1)
        doc2.apply_op(op3)
        self.assertEqual(doc2.get_value([2]), [['multi'],['array']])

        # delete multiple elements
        op4 = Op('ad', [], offset=0, val=4)
        doc2.apply_op(op4)
        self.assertEqual(doc2.snapshot, [None, 42])

        # delete last in list:
        op5 = Op('ad', [], offset=1, val=1)
        doc2.apply_op(op5)
        self.assertEqual(doc2.snapshot, [None])

        # in dicts
        op6 = Op('ad', ['fifth'], offset=2, val=2)
        doc1.apply_op(op6)
        self.assertEqual(doc1.get_value(['fifth']), [55,66])
    def test_boolean_negation(self):
        doc0 =  Document()
        doc0.snapshot = False
        doc1 = self.doc1
        doc2 = self.doc2

        # whole document is a boolean. Just change that
        op1 = Op('bn', [])
        doc0.apply_op(op1)
        self.assertEqual(doc0.snapshot, True)
        doc0.apply_op(op1)
        self.assertEqual(doc0.snapshot, False)

        # boolean at some key/index
        op2 = Op('bn', [4])
        doc2.apply_op(op2)
        self.assertEqual(doc2.get_value([4]), False)
        doc2.apply_op(op2)
        self.assertEqual(doc2.get_value([4]), True)

        # boolean along some path
        path3 = ['fifth',2,'sixth']
        doc1.apply_op(Op('set', path3, val=True))
        op3 = Op('bn', path3)
        doc1.apply_op(op3)
        self.assertEqual(doc1.get_value(path3), False)
        doc1.apply_op(op3)
        self.assertEqual(doc1.get_value(path3), True)
    def test_array_insert(self):
        doc0 =  Document()
        doc0.snapshot = []
        doc1 = self.doc1
        doc2 = self.doc2

        # whole doc is just an empty array. alter it
        op1 = Op('ai', [], val='c', offset=0)
        doc0.apply_op(op1)
        self.assertEqual(doc0.snapshot, ['c'])
        # insert at start
        op2 = Op('ai', [], val='a', offset=0)
        doc0.apply_op(op2)
        self.assertEqual(doc0.snapshot, ['a', 'c'])
        # insert at end
        op3 = Op('ai', [], val='d', offset=2)
        doc0.apply_op(op3)
        self.assertEqual(doc0.snapshot, ['a','c','d'])
        # insert in middle
        op4 = Op('ai', [], val='b', offset=1)
        doc0.apply_op(op4)
        self.assertEqual(doc0.snapshot, ['a','b','c','d'])

        # insert into some array deep in doc
        op5 = Op('ai', [3,1], val='a', offset=1)
        doc2.apply_op(op5)
        self.assertEqual(doc2.get_value([3,1]), ['dimen', 'a'])

        # again
        op6 = Op('ai', ['fifth'], val='a', offset=1)
        doc1.apply_op(op6)
        result6 = [55,'a',66,{'sixth': 'deep string'}, 'rw']
        self.assertEqual(doc1.get_value(['fifth']), result6)
    def test_number_add(self):
        doc0 =  Document()
        doc0.snapshot = 0
        doc1 = self.doc1
        doc2 = self.doc2

        # whole document is just a number. Alter it.
        op1 = Op('na', [], val=5)
        doc0.apply_op(op1)
        self.assertEqual(doc0.snapshot, 5)

        # number deeper in doc
        op2 = Op('na', ['fifth',1], val=-100)
        doc1.apply_op(op2)
        self.assertEqual(doc1.get_value(['fifth',1]), -34)

        # funkier numbers accepted by JSON
        # int frac
        op3 = Op('na', ['fifth',1], val=34.5)
        doc1.apply_op(op3)
        self.assertEqual(doc1.get_value(['fifth',1]), 0.5)