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