1from subdoc_base import SubdocBaseTest
2from lib.mc_bin_client import MemcachedClient, MemcachedError
3from lib.memcacheConstants import *
4import copy, json
5from couchbase_helper.documentgenerator import SubdocDocumentGenerator
6from basetestcase import BaseTestCase
7from random import randint
8
9class SubdocSinglePathTests(SubdocBaseTest):
10    def setUp(self):
11        super(SubdocSinglePathTests, self).setUp()
12        self.client = MemcachedClient(host=self.server.ip)
13        self.jsonSchema = {
14            "id" : "0",
15            "number" : 0,
16            "array" : [],
17            "child" : {},
18            "isDict" : True,
19            "padding": None
20        }
21
22    def tearDown(self):
23        super(SubdocSinglePathTests, self).tearDown()
24
25    def insertBinaryDocument(self, key):
26        pass
27
28    def _createNestedJson(self, key, dict):
29        if dict['levels'] == 0:
30            return
31        if dict['doc'] == {}:
32            dict['doc'] = copy.copy(self.jsonSchema)
33        else:
34            dict['doc']['child'] = copy.copy(self.jsonSchema)
35            dict['doc']['child']['array'] = []
36            dict['doc'] = dict['doc']['child']
37
38        dict['doc']['id'] = key
39        dict['doc']['number'] = dict['levels']
40
41        for level in xrange(0, dict['levels']):
42            dict['doc']['array'].append(level)
43        return self._createNestedJson(key, {'doc': dict['doc'], 'levels': dict['levels']-1})
44
45    def insertJsonDocument(self, key, levels, expiry, size=512):
46        dict = {'doc' : {}, 'levels' : levels }
47        self._createNestedJson(key, dict)
48        jsonDump = json.dumps(dict['doc'])
49        self.client.set(key, expiry, 0, jsonDump)
50
51    def deleteDoc(self, key):
52        self.client.delete(key)
53
54    def _load_all_docs(self, key=None, levels=0, num_items=0):
55        dict = {'doc' : {}, 'levels' : levels }
56        self._createNestedJson(key, dict)
57        template = dict['doc']
58        gen_load = SubdocDocumentGenerator(key, template,start=0, end=num_items)
59
60        print "Inserting json data into bucket"
61        self._load_all_buckets(self.server, gen_load, "create", 0)
62        self._wait_for_stats_all_buckets([self.server])
63
64class SubDocHelper(SubdocSinglePathTests):
65    ''' add helper functions for extending the existing get/replace/insert/counter functions below
66    '''
67
68    def update_docs(self):
69        pass
70
71    def get_docs(self):
72        pass
73
74    def remove_docs(self):
75        pass
76
77    def insert_docs(self):
78        pass
79
80    def validate_results(self):
81        pass
82
83class SanityTests(SubdocSinglePathTests):
84    basicDocKey = 'basicDocKey'
85    deepNestedDocKey =  'deepNestedDocKey'
86    deepNestedGreaterThanAllowedDocKey = 'deepNestedGreaterThanAllowedDocKey'
87
88    def setUp(self):
89        super(SanityTests, self).setUp()
90
91        '''change this to configurable, currently set as 10000 documents'''
92        self._load_all_docs(self.basicDocKey,16,10000)
93        self._load_all_docs(self.deepNestedDocKey,30,1000)
94        self._load_all_docs(self.deepNestedGreaterThanAllowedDocKey,64,100)
95
96        ''' Issue w/ gets, retain below until fixed '''
97        self.insertJsonDocument(self.basicDocKey, 16, 0)
98        self.insertJsonDocument(self.deepNestedDocKey, 30, 0)
99        self.insertJsonDocument(self.deepNestedGreaterThanAllowedDocKey, 64, 0)
100
101    def tearDown(self):
102        super(SanityTests, self).tearDown()
103
104    def getsInDictionaryValue(self):
105        opaque, cas, data = self.client.get_in(self.basicDocKey, 'child.child.child.child.child.isDict')
106        assert data == 'true'
107
108    def getsInDictionaryValueNull(self):
109        opaque, cas, data = self.client.get_in(self.basicDocKey, 'child.child.child.child.child.padding')
110        assert data == 'null'
111
112    def getsInArrayValue(self):
113        opaque, cas, data = self.client.get_in(self.basicDocKey, 'child.child.child.child.child.array[0]')
114        assert data == '0'
115
116    def getsInArrayNegativeIndex(self):
117        opaque, cas, data = self.client.get_in(self.basicDocKey, 'array[-1]')
118        assert data == '15'
119
120    def getsInMismatchPath(self):
121        try:
122            self.client.get_in(self.basicDocKey, 'child[0]')
123        except MemcachedError as error:
124            assert error.status == ERR_SUBDOC_PATH_MISMATCH
125
126    def getsInMissingPath(self):
127        try:
128            self.client.get_in(self.basicDocKey, 'child.random')
129        except MemcachedError as error:
130            assert error.status == ERR_SUBDOC_PATH_ENOENT
131
132    def getsInIncorrectSyntax(self):
133        try:
134            self.client.get_in(self.basicDocKey, 'array[[1]]')
135        except MemcachedError as error:
136            assert error.status == ERR_SUBDOC_PATH_EINVAL
137
138    def getsInNonJsonDocument(self):
139        pass
140
141    def getsInNestedDoc(self):
142        opaque, cas, data = self.client.get_in(self.deepNestedDocKey, 'child.isDict')
143        assert data == 'true'
144
145    def getsInDocTooDeep(self):
146        try:
147            opaque, cas, data = self.client.get_in(self.deepNestedGreaterThanAllowedDocKey, 'child.isDict')
148        except MemcachedError as error:
149            assert error.status == ERR_SUBDOC_DOC_E2DEEP
150
151    def getsInPathTooDeep(self):
152        pass
153
154    ''' Start of Sample Structure Functions'''
155    def removeInArrayValue(self):
156        try:
157            opaque, cas, data = self.client.remove_in(self.basicDocKey, 'child.child.child.child.child.array[0]')
158        except Exception as exception:
159            raise exception
160
161    def removeInNestedDoc(self):
162        try:
163            opaque, cas, data = self.client.remove_in(self.deepNestedDocKey, 'child.child.child.child.child.array[0]')
164        except Exception as exception:
165            raise exception
166
167    def removeInArrayValueNull(self):
168        try:
169            opaque, cas, data = self.client.remove_in(self.basicDocKey, 'child.child.child.child.child.padding')
170        except Exception as exception:
171            raise exception
172
173    def removeInArrayNegativeIndex(self):
174        try:
175            opaque, cas, data = self.client.remove_in(self.basicDocKey, 'array[-1]')
176        except Exception as exception:
177            raise exception
178
179    ''' Does not work - Need to add type '''
180    def counterInNestedDoc(self):
181        try:
182            opaque, cas, data = self.client.counter_in(self.basicDocKey, 'child.child.child.child.child.array[0]','-5',0)
183        except Exception as exception:
184            raise exception
185        finally:
186            opaque, cas, data = self.client.get_in(self.basicDocKey, 'child.child.child.child.child.array[0]')
187
188    def insert_inInArrayValue(self):
189        try:
190            random_string = '"123"'
191            opaque, cas, data = self.client.insert_in(self.basicDocKey, 'child.child.child.child.child.array[0]',random_string)
192        except Exception as exception:
193            raise exception
194
195    def insert_inLongValue(self):
196        try:
197            long_string = ''.join(chr(97 + randint(0, 25)) for i in range(10000))
198            long_string = "'" + long_string + "'"
199            opaque, cas, data = self.client.insert_in(self.basicDocKey, 'child.child.child.child.child.array[0]',long_string)
200        except Exception as exception:
201            raise exception
202
203    def insert_inNestedDoc(self):
204        try:
205            long_string = ''.join(chr(97 + randint(0, 25)) for i in range(10000))
206            long_string = "'" + long_string + "'"
207            opaque, cas, data = self.client.insert_in(self.deepNestedDocKey, 'child.isDict',long_string)
208        except Exception as exception:
209            raise exception
210
211    def replace_inInArrayValue(self):
212        try:
213            random_string = '"hello"'
214            opaque, cas, data = self.client.replace_in(self.basicDocKey, 'child.child.child.child.child.array[0]',random_string)
215        except Exception as exception:
216            raise exception
217
218    def replace_inLongValue(self):
219        try:
220            long_string = ''.join(chr(97 + randint(0, 25)) for i in range(10000))
221            long_string = "'" + long_string + "'"
222            opaque, cas, data = self.client.replace_in(self.basicDocKey, 'child.child.child.child.child.child.child.child.array[0]',long_string)
223        except Exception as exception:
224            raise exception
225
226    def replace_inNullValue(self):
227        try:
228            empty_string = "'   '"
229            opaque, cas, data = self.client.replace_in(self.basicDocKey, 'child.child.child.child.child.child.child.child.array[0]',empty_string)
230        except Exception as exception:
231            raise exception
232
233    def exists_inInArrayValue(self):
234        try:
235            opaque, cas, data = self.client.exists_in(self.deepNestedDocKey,'child.child.child.child.child.array[0]')
236        except Exception as exception:
237            raise exception
238
239    def exists_inInArrayNegativeIndex(self):
240        try:
241            opaque, cas, data = self.client.exists_in(self.deepNestedDocKey,'child.child.child.child.child.array[-1]')
242        except Exception as exception:
243            raise exception
244
245    def exists_Missing(self):
246        try:
247            opaque, cas, data = self.client.exists_in(self.deepNestedDocKey,'child.child.child.child.child.array.child')
248        except MemcachedError as error:
249            assert error.status == ERR_SUBDOC_PATH_MISMATCH
250
251    ''' Error- Path Mismatch'''
252    def append_inInArrayValue(self):
253        try:
254            random_string = '"hello"'
255            blank_string = '" "'
256            opaque, cas, data = self.client.append_in(self.basicDocKey, 'child.child.child.child.child.array[0]',1)
257        except Exception as exception:
258            raise exception
259
260    ''' Error- Path Mismatch'''
261    def prepend_inInArrayValue(self):
262        try:
263            random_string = '"hello"'
264            blank_string = '" "'
265            opaque, cas, data = self.client.prepend_in(self.basicDocKey, 'child.child.child.child.child.array[0]',random_string)
266        except Exception as exception:
267            raise exception
268
269    ''' End of Sample Structure Functions'''
270