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