Add support for a realtime sorcery module.
[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 0;
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 ao2_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") ||
216                 ast_sorcery_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(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
223         ast_sorcery_object_field_register(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 static int unload_module(void)
757 {
758         ast_config_engine_deregister(&sorcery_config_engine);
759         AST_TEST_UNREGISTER(object_create);
760         AST_TEST_UNREGISTER(object_retrieve_id);
761         AST_TEST_UNREGISTER(object_retrieve_field);
762         AST_TEST_UNREGISTER(object_retrieve_multiple_all);
763         AST_TEST_UNREGISTER(object_retrieve_multiple_field);
764         AST_TEST_UNREGISTER(object_retrieve_regex);
765         AST_TEST_UNREGISTER(object_update);
766         AST_TEST_UNREGISTER(object_update_uncreated);
767         AST_TEST_UNREGISTER(object_delete);
768         AST_TEST_UNREGISTER(object_delete_uncreated);
769
770         return 0;
771 }
772
773 static int load_module(void)
774 {
775         ast_config_engine_register(&sorcery_config_engine);
776         ast_realtime_append_mapping("sorcery_realtime_test", "sorcery_realtime_test", "test", "test", 1);
777         AST_TEST_REGISTER(object_create);
778         AST_TEST_REGISTER(object_retrieve_id);
779         AST_TEST_REGISTER(object_retrieve_field);
780         AST_TEST_REGISTER(object_retrieve_multiple_all);
781         AST_TEST_REGISTER(object_retrieve_multiple_field);
782         AST_TEST_REGISTER(object_retrieve_regex);
783         AST_TEST_REGISTER(object_update);
784         AST_TEST_REGISTER(object_update_uncreated);
785         AST_TEST_REGISTER(object_delete);
786         AST_TEST_REGISTER(object_delete_uncreated);
787
788         return AST_MODULE_LOAD_SUCCESS;
789 }
790
791 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery Realtime Wizard test module");