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