res_sorcery_realtime: fix bug when successful UPDATE is treated as failed
[asterisk/asterisk.git] / tests / test_sorcery_realtime.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Sorcery Unit Tests
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  */
26
27 /*** MODULEINFO
28         <depend>TEST_FRAMEWORK</depend>
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_REGISTER_FILE()
35
36 #include "asterisk/test.h"
37 #include "asterisk/module.h"
38 #include "asterisk/sorcery.h"
39 #include "asterisk/astdb.h"
40 #include "asterisk/logger.h"
41
42 /*! \brief Configuration structure which contains all stored objects */
43 static struct ast_config *realtime_objects;
44
45 static struct ast_variable *realtime_sorcery(const char *database, const char *table, const struct ast_variable *fields)
46 {
47         char *object_id = NULL;
48
49         while ((object_id = ast_category_browse(realtime_objects, object_id))) {
50                 if (!ast_variable_lists_match(ast_category_root(realtime_objects, object_id), fields, 0)) {
51                         continue;
52                 }
53
54                 return ast_variables_dup(ast_category_root(realtime_objects, object_id));
55         }
56
57         return NULL;
58 }
59
60 static struct ast_config *realtime_sorcery_multi(const char *database, const char *table, const struct ast_variable *fields)
61 {
62         struct ast_config *objects;
63         char *object_id = NULL;
64
65         if (!(objects = ast_config_new())) {
66                 return NULL;
67         }
68
69         while ((object_id = ast_category_browse(realtime_objects, object_id))) {
70                 struct ast_category *object;
71                 const struct ast_variable *object_fields = ast_category_root(realtime_objects, object_id);
72
73                 if (!ast_variable_lists_match(object_fields, fields, 0)) {
74                         continue;
75                 }
76
77                 if (!(object = ast_category_new("", "", 0))) {
78                         ast_config_destroy(objects);
79                         return NULL;
80                 }
81
82                 ast_variable_append(object, ast_variables_dup(ast_category_root(realtime_objects, object_id)));
83                 ast_category_append(objects, object);
84         }
85
86         return objects;
87 }
88
89 static int realtime_sorcery_update(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
90 {
91         struct ast_category *object, *found;
92
93         if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
94                 return 0;
95         } else if (!(object = ast_category_new(entity, "", 0))) {
96                 return -1;
97         }
98
99         ast_category_delete(realtime_objects, found);
100         ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
101         ast_variable_append(object, ast_variable_new(keyfield, entity, ""));
102         ast_category_append(realtime_objects, object);
103
104         return 1;
105 }
106
107 static int realtime_sorcery_store(const char *database, const char *table, const struct ast_variable *fields)
108 {
109         /* The key field is explicit within res_sorcery_realtime */
110         const struct ast_variable *keyfield = ast_variable_find_variable_in_list(fields, "id");
111         struct ast_category *object;
112
113         if (!keyfield || ast_category_exist(realtime_objects, keyfield->value, NULL) || !(object = ast_category_new(keyfield->value, "", 0))) {
114                 return -1;
115         }
116
117         ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
118         ast_category_append(realtime_objects, object);
119
120         return 1;
121 }
122
123 static int realtime_sorcery_destroy(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
124 {
125         struct ast_category *found;
126         if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
127                 return 0;
128         }
129
130         ast_category_delete(realtime_objects, found);
131
132         return 1;
133 }
134
135 struct ast_config_engine sorcery_config_engine = {
136         .name = "sorcery_realtime_test",
137         .realtime_func = realtime_sorcery,
138         .realtime_multi_func = realtime_sorcery_multi,
139         .update_func = realtime_sorcery_update,
140         .store_func = realtime_sorcery_store,
141         .destroy_func = realtime_sorcery_destroy,
142 };
143
144 /*! \brief Dummy sorcery object */
145 struct test_sorcery_object {
146         SORCERY_OBJECT(details);
147         unsigned int bob;
148         unsigned int joe;
149 };
150
151 /*! \brief Internal function to allocate a test object */
152 static void *test_sorcery_object_alloc(const char *id)
153 {
154         return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
155 }
156
157 static struct ast_sorcery *alloc_and_initialize_sorcery(char *table)
158 {
159         struct ast_sorcery *sorcery;
160
161         if (!(sorcery = ast_sorcery_open())) {
162                 return NULL;
163         }
164
165         if ((ast_sorcery_apply_default(sorcery, "test", "realtime", table) != AST_SORCERY_APPLY_SUCCESS) ||
166                 ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL) ||
167                 !(realtime_objects = ast_config_new())) {
168                 ast_sorcery_unref(sorcery);
169                 return NULL;
170         }
171
172         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
173         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
174
175         return sorcery;
176 }
177
178 static void deinitialize_sorcery(struct ast_sorcery *sorcery)
179 {
180         ast_config_destroy(realtime_objects);
181         realtime_objects = NULL;
182         ast_sorcery_unref(sorcery);
183 }
184
185 AST_TEST_DEFINE(object_create)
186 {
187         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
188         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
189
190         switch (cmd) {
191         case TEST_INIT:
192                 info->name = "object_create";
193                 info->category = "/res/sorcery_realtime/";
194                 info->summary = "sorcery realtime object creation unit test";
195                 info->description =
196                         "Test object creation in sorcery using realtime wizard";
197                 return AST_TEST_NOT_RUN;
198         case TEST_EXECUTE:
199                 break;
200         }
201
202         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
203                 ast_test_status_update(test, "Failed to open sorcery structure\n");
204                 return AST_TEST_FAIL;
205         }
206
207         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
208                 ast_test_status_update(test, "Failed to allocate a known object type\n");
209                 return AST_TEST_FAIL;
210         }
211
212         if (ast_sorcery_create(sorcery, obj)) {
213                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
214                 return AST_TEST_FAIL;
215         }
216
217         return AST_TEST_PASS;
218 }
219
220 AST_TEST_DEFINE(object_retrieve_id)
221 {
222         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
223         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
224
225         switch (cmd) {
226         case TEST_INIT:
227                 info->name = "object_retrieve_id";
228                 info->category = "/res/sorcery_realtime/";
229                 info->summary = "sorcery object retrieval using id unit test";
230                 info->description =
231                         "Test object retrieval using id in sorcery with realtime wizard";
232                 return AST_TEST_NOT_RUN;
233         case TEST_EXECUTE:
234                 break;
235         }
236
237         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
238                 ast_test_status_update(test, "Failed to open sorcery structure\n");
239                 return AST_TEST_FAIL;
240         }
241
242         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
243                 ast_test_status_update(test, "Failed to allocate a known object type\n");
244                 return AST_TEST_FAIL;
245         }
246
247         if (ast_sorcery_create(sorcery, obj)) {
248                 ast_test_status_update(test, "Failed to create object using astdb wizard\n");
249                 return AST_TEST_FAIL;
250         }
251
252         ao2_cleanup(obj);
253
254         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
255                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
256                 return AST_TEST_FAIL;
257         }
258
259         if (ast_sorcery_create(sorcery, obj)) {
260                 ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
261                 return AST_TEST_FAIL;
262         }
263
264         ao2_cleanup(obj);
265
266         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
267                 ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
268                 return AST_TEST_FAIL;
269         } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
270                 ast_test_status_update(test, "Retrieved object does not have correct id\n");
271                 return AST_TEST_FAIL;
272         }
273
274         return AST_TEST_PASS;
275 }
276
277 AST_TEST_DEFINE(object_retrieve_field)
278 {
279         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
280         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
281         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "42", ""), ast_variables_destroy);
282
283         switch (cmd) {
284         case TEST_INIT:
285                 info->name = "object_retrieve_field";
286                 info->category = "/res/sorcery_realtime/";
287                 info->summary = "sorcery object retrieval using a specific field unit test";
288                 info->description =
289                         "Test object retrieval using a specific field in sorcery with realtime wizard";
290                 return AST_TEST_NOT_RUN;
291         case TEST_EXECUTE:
292                 break;
293         }
294
295         if (!fields) {
296                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
297                 return AST_TEST_FAIL;
298         }
299
300         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
301                 ast_test_status_update(test, "Failed to open sorcery structure\n");
302                 return AST_TEST_FAIL;
303         }
304
305         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
306                 ast_test_status_update(test, "Failed to allocate a known object type\n");
307                 return AST_TEST_FAIL;
308         }
309
310         obj->joe = 42;
311
312         if (ast_sorcery_create(sorcery, obj)) {
313                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
314                 return AST_TEST_FAIL;
315         }
316
317         ao2_cleanup(obj);
318
319         if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
320                 ast_test_status_update(test, "Failed to retrieve properly created object using 'joe' field\n");
321                 return AST_TEST_FAIL;
322         }
323
324         ao2_cleanup(obj);
325         ast_variables_destroy(fields);
326
327         if (!(fields = ast_variable_new("joe", "49", ""))) {
328                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
329                 return AST_TEST_FAIL;
330         }
331
332         if ((obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
333                 ast_test_status_update(test, "Retrieved an object using a field with an in-correct value... that should not happen\n");
334                 return AST_TEST_FAIL;
335         }
336
337         return AST_TEST_PASS;
338 }
339
340 AST_TEST_DEFINE(object_retrieve_multiple_all)
341 {
342         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
343         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
344         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
345
346         switch (cmd) {
347         case TEST_INIT:
348                 info->name = "object_retrieve_multiple_all";
349                 info->category = "/res/sorcery_realtime/";
350                 info->summary = "sorcery multiple object retrieval unit test";
351                 info->description =
352                         "Test multiple object retrieval in sorcery using realtime wizard";
353                 return AST_TEST_NOT_RUN;
354         case TEST_EXECUTE:
355                 break;
356         }
357
358         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
359                 ast_test_status_update(test, "Failed to open sorcery structure\n");
360                 return AST_TEST_FAIL;
361         }
362
363         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
364                 ast_test_status_update(test, "Failed to allocate a known object type\n");
365                 return AST_TEST_FAIL;
366         }
367
368         if (ast_sorcery_create(sorcery, obj)) {
369                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
370                 return AST_TEST_FAIL;
371         }
372
373         ao2_cleanup(obj);
374
375         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
376                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
377                 return AST_TEST_FAIL;
378         }
379
380         if (ast_sorcery_create(sorcery, obj)) {
381                 ast_test_status_update(test, "Failed to create second object using realtime wizard\n");
382                 return AST_TEST_FAIL;
383         }
384
385         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
386                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
387                 return AST_TEST_FAIL;
388         } else if (ao2_container_count(objects) != 2) {
389                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
390                 return AST_TEST_FAIL;
391         }
392
393         return AST_TEST_PASS;
394 }
395
396 AST_TEST_DEFINE(object_retrieve_multiple_all_nofetch)
397 {
398         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
399         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
400         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
401
402         switch (cmd) {
403         case TEST_INIT:
404                 info->name = "object_retrieve_multiple_all_nofetch";
405                 info->category = "/res/sorcery_realtime/";
406                 info->summary = "sorcery multiple object retrieval unit test";
407                 info->description =
408                         "Test multiple object retrieval in sorcery using realtime wizard";
409                 return AST_TEST_NOT_RUN;
410         case TEST_EXECUTE:
411                 break;
412         }
413
414         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
415                 ast_test_status_update(test, "Failed to open sorcery structure\n");
416                 return AST_TEST_FAIL;
417         }
418
419         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
420                 ast_test_status_update(test, "Failed to allocate a known object type\n");
421                 return AST_TEST_FAIL;
422         }
423
424         if (ast_sorcery_create(sorcery, obj)) {
425                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
426                 return AST_TEST_FAIL;
427         }
428
429         ao2_cleanup(obj);
430
431         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
432                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
433                 return AST_TEST_FAIL;
434         }
435
436         if (ast_sorcery_create(sorcery, obj)) {
437                 ast_test_status_update(test, "Failed to create second object using realtime wizard\n");
438                 return AST_TEST_FAIL;
439         }
440
441         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
442                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
443                 return AST_TEST_FAIL;
444         } else if (ao2_container_count(objects) != 0) {
445                 ast_test_status_update(test, "Received a container with objects in it when there should be none\n");
446                 return AST_TEST_FAIL;
447         }
448
449         return AST_TEST_PASS;
450 }
451
452
453 AST_TEST_DEFINE(object_retrieve_multiple_field)
454 {
455         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
456         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
457         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
458         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "6", ""), ast_variables_destroy);
459
460         switch (cmd) {
461         case TEST_INIT:
462                 info->name = "object_retrieve_multiple_field";
463                 info->category = "/res/sorcery_realtime/";
464                 info->summary = "sorcery multiple object retrieval unit test";
465                 info->description =
466                         "Test multiple object retrieval in sorcery using fields using realtime wizard";
467                 return AST_TEST_NOT_RUN;
468         case TEST_EXECUTE:
469                 break;
470         }
471
472         if (!fields) {
473                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
474                 return AST_TEST_FAIL;
475         }
476
477         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
478                 ast_test_status_update(test, "Failed to open sorcery structure\n");
479                 return AST_TEST_FAIL;
480         }
481
482         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
483                 ast_test_status_update(test, "Failed to allocate a known object type\n");
484                 return AST_TEST_FAIL;
485         }
486
487         obj->joe = 6;
488
489         if (ast_sorcery_create(sorcery, obj)) {
490                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
491                 return AST_TEST_FAIL;
492         }
493
494         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
495                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
496                 return AST_TEST_FAIL;
497         } else if (ao2_container_count(objects) != 1) {
498                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
499                 return AST_TEST_FAIL;
500         }
501
502         ao2_cleanup(objects);
503         ast_variables_destroy(fields);
504
505         if (!(fields = ast_variable_new("joe", "7", ""))) {
506                 ast_test_status_update(test, "Failed to create fields for multiple retrieval\n");
507                 return AST_TEST_FAIL;
508         } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
509                 ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
510                 return AST_TEST_FAIL;
511         } else if (ao2_container_count(objects)) {
512                 ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
513                 return AST_TEST_FAIL;
514         }
515
516         return AST_TEST_PASS;
517 }
518
519 AST_TEST_DEFINE(object_retrieve_regex)
520 {
521         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
522         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
523         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
524
525         switch (cmd) {
526         case TEST_INIT:
527                 info->name = "object_retrieve_regex";
528                 info->category = "/res/sorcery_realtime/";
529                 info->summary = "sorcery multiple object retrieval using regex unit test";
530                 info->description =
531                         "Test multiple object retrieval in sorcery using regular expression for matching using realtime wizard";
532                 return AST_TEST_NOT_RUN;
533         case TEST_EXECUTE:
534                 break;
535         }
536
537         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
538                 ast_test_status_update(test, "Failed to open sorcery structure\n");
539                 return AST_TEST_FAIL;
540         }
541
542         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
543                 ast_test_status_update(test, "Failed to allocate a known object type\n");
544                 return AST_TEST_FAIL;
545         }
546
547         if (ast_sorcery_create(sorcery, obj)) {
548                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
549                 return AST_TEST_FAIL;
550         }
551
552         ao2_cleanup(obj);
553
554         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
555                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
556                 return AST_TEST_FAIL;
557         }
558
559         if (ast_sorcery_create(sorcery, obj)) {
560                 ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
561                 return AST_TEST_FAIL;
562         }
563
564         ao2_cleanup(obj);
565
566         if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
567                 ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
568                 return AST_TEST_FAIL;
569         }
570
571         if (ast_sorcery_create(sorcery, obj)) {
572                 ast_test_status_update(test, "Failed to create third object using astdb wizard\n");
573                 return AST_TEST_FAIL;
574         }
575
576         if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "blah-"))) {
577                 ast_test_status_update(test, "Failed to retrieve a container of objects\n");
578                 return AST_TEST_FAIL;
579         } else if (ao2_container_count(objects) != 2) {
580                 ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 2\n", ao2_container_count(objects));
581                 return AST_TEST_FAIL;
582         }
583
584         return AST_TEST_PASS;
585 }
586
587 AST_TEST_DEFINE(object_retrieve_regex_nofetch)
588 {
589         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
590         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
591         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
592
593         switch (cmd) {
594         case TEST_INIT:
595                 info->name = "object_retrieve_regex_nofetch";
596                 info->category = "/res/sorcery_realtime/";
597                 info->summary = "sorcery multiple object retrieval using regex unit test";
598                 info->description =
599                         "Test multiple object retrieval in sorcery using regular expression for matching using realtime wizard";
600                 return AST_TEST_NOT_RUN;
601         case TEST_EXECUTE:
602                 break;
603         }
604
605         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
606                 ast_test_status_update(test, "Failed to open sorcery structure\n");
607                 return AST_TEST_FAIL;
608         }
609
610         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
611                 ast_test_status_update(test, "Failed to allocate a known object type\n");
612                 return AST_TEST_FAIL;
613         }
614
615         if (ast_sorcery_create(sorcery, obj)) {
616                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
617                 return AST_TEST_FAIL;
618         }
619
620         ao2_cleanup(obj);
621
622         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
623                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
624                 return AST_TEST_FAIL;
625         }
626
627         if (ast_sorcery_create(sorcery, obj)) {
628                 ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
629                 return AST_TEST_FAIL;
630         }
631
632         ao2_cleanup(obj);
633
634         if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
635                 ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
636                 return AST_TEST_FAIL;
637         }
638
639         if (ast_sorcery_create(sorcery, obj)) {
640                 ast_test_status_update(test, "Failed to create third object using astdb wizard\n");
641                 return AST_TEST_FAIL;
642         }
643
644         if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", ""))) {
645                 ast_test_status_update(test, "Failed to retrieve a container of objects\n");
646                 return AST_TEST_FAIL;
647         } else if (ao2_container_count(objects) != 0) {
648                 ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 0\n", ao2_container_count(objects));
649                 return AST_TEST_FAIL;
650         }
651
652         return AST_TEST_PASS;
653 }
654
655 AST_TEST_DEFINE(object_update)
656 {
657         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
658         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
659         RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
660
661         switch (cmd) {
662         case TEST_INIT:
663                 info->name = "object_update";
664                 info->category = "/res/sorcery_realtime/";
665                 info->summary = "sorcery object update unit test";
666                 info->description =
667                         "Test object updating in sorcery using realtime wizard";
668                 return AST_TEST_NOT_RUN;
669         case TEST_EXECUTE:
670                 break;
671         }
672
673         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
674                 ast_test_status_update(test, "Failed to open sorcery structure\n");
675                 return AST_TEST_FAIL;
676         }
677
678         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
679                 ast_test_status_update(test, "Failed to allocate a known object type\n");
680                 return AST_TEST_FAIL;
681         }
682
683         if (ast_sorcery_create(sorcery, obj)) {
684                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
685                 return AST_TEST_FAIL;
686         }
687
688         if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
689                 ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
690                 return AST_TEST_FAIL;
691         }
692
693         ao2_cleanup(obj);
694
695         obj2->bob = 1000;
696         obj2->joe = 2000;
697
698         if (ast_sorcery_update(sorcery, obj2)) {
699                 ast_test_status_update(test, "Failed to update sorcery with new object\n");
700                 return AST_TEST_FAIL;
701         }
702
703         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
704                 ast_test_status_update(test, "Failed to retrieve properly updated object\n");
705                 return AST_TEST_FAIL;
706         } else if ((obj->bob != obj2->bob) || (obj->joe != obj2->joe)) {
707                 ast_test_status_update(test, "Object retrieved is not the updated object\n");
708                 return AST_TEST_FAIL;
709         }
710
711         return AST_TEST_PASS;
712 }
713
714 AST_TEST_DEFINE(object_delete)
715 {
716         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
717         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
718
719         switch (cmd) {
720         case TEST_INIT:
721                 info->name = "object_delete";
722                 info->category = "/res/sorcery_realtime/";
723                 info->summary = "sorcery object deletion unit test";
724                 info->description =
725                         "Test object deletion in sorcery using realtime wizard";
726                 return AST_TEST_NOT_RUN;
727         case TEST_EXECUTE:
728                 break;
729         }
730
731         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
732                 ast_test_status_update(test, "Failed to open sorcery structure\n");
733                 return AST_TEST_FAIL;
734         }
735
736         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
737                 ast_test_status_update(test, "Failed to allocate a known object type\n");
738                 return AST_TEST_FAIL;
739         }
740
741         if (ast_sorcery_create(sorcery, obj)) {
742                 ast_test_status_update(test, "Failed to create object using realtime wizard\n");
743                 return AST_TEST_FAIL;
744         }
745
746         if (ast_sorcery_delete(sorcery, obj)) {
747                 ast_test_status_update(test, "Failed to delete object using realtime wizard\n");
748                 return AST_TEST_FAIL;
749         }
750
751         ao2_cleanup(obj);
752
753         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
754                 ast_test_status_update(test, "Retrieved deleted object that should not be there\n");
755                 return AST_TEST_FAIL;
756         }
757
758         return AST_TEST_PASS;
759 }
760
761 AST_TEST_DEFINE(object_delete_uncreated)
762 {
763         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
764         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
765
766         switch (cmd) {
767         case TEST_INIT:
768                 info->name = "object_delete_uncreated";
769                 info->category = "/res/sorcery_realtime/";
770                 info->summary = "sorcery object deletion unit test";
771                 info->description =
772                         "Test object deletion of an uncreated object in sorcery using realtime wizard";
773                 return AST_TEST_NOT_RUN;
774         case TEST_EXECUTE:
775                 break;
776         }
777
778         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
779                 ast_test_status_update(test, "Failed to open sorcery structure\n");
780                 return AST_TEST_FAIL;
781         }
782
783         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
784                 ast_test_status_update(test, "Failed to allocate a known object type\n");
785                 return AST_TEST_FAIL;
786         }
787
788         if (!ast_sorcery_delete(sorcery, obj)) {
789                 ast_test_status_update(test, "Successfully deleted an object which was never created\n");
790                 return AST_TEST_FAIL;
791         }
792
793         return AST_TEST_PASS;
794 }
795
796 AST_TEST_DEFINE(object_allocate_on_retrieval)
797 {
798         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
799         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
800         struct ast_category *cat;
801
802         switch (cmd) {
803         case TEST_INIT:
804                 info->name = "object_allocate_on_retrieval";
805                 info->category = "/res/sorcery_realtime/";
806                 info->summary = "sorcery object allocation upon retrieval unit test";
807                 info->description =
808                         "This test creates data in a realtime backend, not through sorcery. Sorcery is then\n"
809                         "instructed to retrieve an object with the id of the object that was created in the\n"
810                         "realtime backend. Sorcery should be able to allocate the object appropriately";
811                 return AST_TEST_NOT_RUN;
812         case TEST_EXECUTE:
813                 break;
814         }
815
816         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
817                 ast_test_status_update(test, "Failed to open sorcery structure\n");
818                 return AST_TEST_FAIL;
819         }
820
821         cat = ast_category_new("blah", "", 0);
822         ast_variable_append(cat, ast_variable_new("id", "blah", ""));
823         ast_variable_append(cat, ast_variable_new("bob", "42", ""));
824         ast_variable_append(cat, ast_variable_new("joe", "93", ""));
825         ast_category_append(realtime_objects, cat);
826
827         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
828                 ast_test_status_update(test, "Failed to allocate object 'blah' base on realtime data\n");
829                 return AST_TEST_FAIL;
830         }
831
832         if (obj->bob != 42) {
833                 ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
834                                 obj->bob);
835                 return AST_TEST_FAIL;
836         } else if (obj->joe != 93) {
837                 ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
838                                 obj->joe);
839                 return AST_TEST_FAIL;
840         }
841
842         return AST_TEST_PASS;
843 }
844
845
846 AST_TEST_DEFINE(object_filter)
847 {
848         RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
849         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
850         struct ast_category *cat;
851
852         switch (cmd) {
853         case TEST_INIT:
854                 info->name = "object_filter";
855                 info->category = "/res/sorcery_realtime/";
856                 info->summary = "sorcery object field filter unit test";
857                 info->description =
858                         "This test creates data in a realtime backend, not through sorcery. In addition to\n"
859                         "the object fields that have been registered with sorcery, there is data in the\n"
860                         "realtime backend that is unknown to sorcery. When sorcery attempts to retrieve\n"
861                         "the object from the realtime backend, the data unknown to sorcery should be\n"
862                         "filtered out of the returned objectset, and the object should be successfully\n"
863                         "allocated by sorcery";
864                 return AST_TEST_NOT_RUN;
865         case TEST_EXECUTE:
866                 break;
867         }
868
869         if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
870                 ast_test_status_update(test, "Failed to open sorcery structure\n");
871                 return AST_TEST_FAIL;
872         }
873
874         cat = ast_category_new("blah", "", 0);
875         ast_variable_append(cat, ast_variable_new("id", "blah", ""));
876         ast_variable_append(cat, ast_variable_new("bob", "42", ""));
877         ast_variable_append(cat, ast_variable_new("joe", "93", ""));
878         ast_variable_append(cat, ast_variable_new("fred", "50", ""));
879         ast_category_append(realtime_objects, cat);
880
881         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
882                 ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
883                 return AST_TEST_FAIL;
884         }
885
886         if (obj->bob != 42) {
887                 ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
888                                 obj->bob);
889                 return AST_TEST_FAIL;
890         } else if (obj->joe != 93) {
891                 ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
892                                 obj->joe);
893                 return AST_TEST_FAIL;
894         }
895         return AST_TEST_PASS;
896 }
897
898 static int unload_module(void)
899 {
900         ast_config_engine_deregister(&sorcery_config_engine);
901         AST_TEST_UNREGISTER(object_create);
902         AST_TEST_UNREGISTER(object_retrieve_id);
903         AST_TEST_UNREGISTER(object_retrieve_field);
904         AST_TEST_UNREGISTER(object_retrieve_multiple_all);
905         AST_TEST_UNREGISTER(object_retrieve_multiple_all_nofetch);
906         AST_TEST_UNREGISTER(object_retrieve_multiple_field);
907         AST_TEST_UNREGISTER(object_retrieve_regex);
908         AST_TEST_UNREGISTER(object_retrieve_regex_nofetch);
909         AST_TEST_UNREGISTER(object_update);
910         AST_TEST_UNREGISTER(object_delete);
911         AST_TEST_UNREGISTER(object_delete_uncreated);
912         AST_TEST_UNREGISTER(object_allocate_on_retrieval);
913         AST_TEST_UNREGISTER(object_filter);
914
915         return 0;
916 }
917
918 static int load_module(void)
919 {
920         ast_config_engine_register(&sorcery_config_engine);
921         ast_realtime_append_mapping("sorcery_realtime_test", "sorcery_realtime_test", "test", "test", 1);
922         AST_TEST_REGISTER(object_create);
923         AST_TEST_REGISTER(object_retrieve_id);
924         AST_TEST_REGISTER(object_retrieve_field);
925         AST_TEST_REGISTER(object_retrieve_multiple_all);
926         AST_TEST_REGISTER(object_retrieve_multiple_all_nofetch);
927         AST_TEST_REGISTER(object_retrieve_multiple_field);
928         AST_TEST_REGISTER(object_retrieve_regex);
929         AST_TEST_REGISTER(object_retrieve_regex_nofetch);
930         AST_TEST_REGISTER(object_update);
931         AST_TEST_REGISTER(object_delete);
932         AST_TEST_REGISTER(object_delete_uncreated);
933         AST_TEST_REGISTER(object_allocate_on_retrieval);
934         AST_TEST_REGISTER(object_filter);
935
936         return AST_MODULE_LOAD_SUCCESS;
937 }
938
939 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery Realtime Wizard test module");