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