Merge "ari: Implement 'debug all' and request/response logging"
[asterisk/asterisk.git] / tests / test_bucket.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 Bucket Unit Tests
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  */
26
27 /*** MODULEINFO
28         <depend>TEST_FRAMEWORK</depend>
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 #include <sys/stat.h>
35
36 #include "asterisk/test.h"
37 #include "asterisk/module.h"
38 #include "asterisk/bucket.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/json.h"
41 #include "asterisk/file.h"
42
43 /*! \brief Test state structure for scheme wizards */
44 struct bucket_test_state {
45         /*! \brief Whether the object has been created or not */
46         unsigned int created:1;
47         /*! \brief Whether the object has been updated or not */
48         unsigned int updated:1;
49         /*! \brief Whether the object has been deleted or not */
50         unsigned int deleted:1;
51         /*! \brief Whether the object is stale or not */
52         unsigned int is_stale:1;
53 };
54
55 /*! \brief Global scope structure for testing bucket wizards */
56 static struct bucket_test_state bucket_test_wizard_state;
57
58 static void bucket_test_wizard_clear(void)
59 {
60         bucket_test_wizard_state.created = 0;
61         bucket_test_wizard_state.updated = 0;
62         bucket_test_wizard_state.deleted = 0;
63         bucket_test_wizard_state.is_stale = 0;
64 }
65
66 static int bucket_test_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
67 {
68         if (bucket_test_wizard_state.created) {
69                 return -1;
70         }
71
72         bucket_test_wizard_state.created = 1;
73
74         return 0;
75 }
76
77 static int bucket_test_wizard_update(const struct ast_sorcery *sorcery, void *data, void *object)
78 {
79         if (bucket_test_wizard_state.updated) {
80                 return -1;
81         }
82
83         bucket_test_wizard_state.updated = 1;
84
85         return 0;
86 }
87
88 static void *bucket_test_wizard_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
89         const char *id)
90 {
91         if (!strcmp(type, "bucket")) {
92                 return ast_bucket_alloc(id);
93         } else if (!strcmp(type, "file")) {
94                 return ast_bucket_file_alloc(id);
95         } else {
96                 return NULL;
97         }
98 }
99
100 static int bucket_test_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
101 {
102         if (bucket_test_wizard_state.deleted) {
103                 return -1;
104         }
105
106         bucket_test_wizard_state.deleted = 1;
107
108         return 0;
109 }
110
111 static int bucket_test_wizard_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
112 {
113         return bucket_test_wizard_state.is_stale;
114 }
115
116 static struct ast_sorcery_wizard bucket_test_wizard = {
117         .name = "test",
118         .create = bucket_test_wizard_create,
119         .retrieve_id = bucket_test_wizard_retrieve_id,
120         .delete = bucket_test_wizard_delete,
121         .is_stale = bucket_test_wizard_is_stale,
122 };
123
124 static struct ast_sorcery_wizard bucket_file_test_wizard = {
125         .name = "test",
126         .create = bucket_test_wizard_create,
127         .update = bucket_test_wizard_update,
128         .retrieve_id = bucket_test_wizard_retrieve_id,
129         .delete = bucket_test_wizard_delete,
130         .is_stale = bucket_test_wizard_is_stale,
131 };
132
133 AST_TEST_DEFINE(bucket_scheme_register)
134 {
135         switch (cmd) {
136         case TEST_INIT:
137                 info->name = "bucket_scheme_register_unregister";
138                 info->category = "/main/bucket/";
139                 info->summary = "bucket scheme registration/unregistration unit test";
140                 info->description =
141                         "Test registration and unregistration of bucket scheme";
142                 return AST_TEST_NOT_RUN;
143         case TEST_EXECUTE:
144                 break;
145         }
146
147         if (!ast_bucket_scheme_register("", NULL, NULL, NULL, NULL)) {
148                 ast_test_status_update(test, "Successfully registered a Bucket scheme without name or wizards\n");
149                 return AST_TEST_FAIL;
150         }
151
152         if (!ast_bucket_scheme_register("test", &bucket_test_wizard, &bucket_file_test_wizard, NULL, NULL)) {
153                 ast_test_status_update(test, "Successfully registered a Bucket scheme twice\n");
154                 return AST_TEST_FAIL;
155         }
156
157         return AST_TEST_PASS;
158 }
159
160 AST_TEST_DEFINE(bucket_alloc)
161 {
162         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
163
164         switch (cmd) {
165         case TEST_INIT:
166                 info->name = "bucket_alloc";
167                 info->category = "/main/bucket/";
168                 info->summary = "bucket allocation unit test";
169                 info->description =
170                         "Test allocation of buckets";
171                 return AST_TEST_NOT_RUN;
172         case TEST_EXECUTE:
173                 break;
174         }
175
176         if ((bucket = ast_bucket_alloc(""))) {
177                 ast_test_status_update(test, "Allocated a bucket with no URI provided\n");
178                 return AST_TEST_FAIL;
179         }
180
181         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
182                 ast_test_status_update(test, "Failed to allocate bucket\n");
183                 return AST_TEST_FAIL;
184         }
185
186         if (strcmp(ast_sorcery_object_get_id(bucket), "test:///tmp/bob")) {
187                 ast_test_status_update(test, "URI within allocated bucket is '%s' and should be test:///tmp/bob\n",
188                         ast_sorcery_object_get_id(bucket));
189                 return AST_TEST_FAIL;
190         }
191
192         if (strcmp(bucket->scheme, "test")) {
193                 ast_test_status_update(test, "Scheme within allocated bucket is '%s' and should be test\n",
194                         bucket->scheme);
195                 return AST_TEST_FAIL;
196         }
197
198         return AST_TEST_PASS;
199 }
200
201 AST_TEST_DEFINE(bucket_create)
202 {
203         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
204
205         switch (cmd) {
206         case TEST_INIT:
207                 info->name = "bucket_create";
208                 info->category = "/main/bucket/";
209                 info->summary = "bucket creation unit test";
210                 info->description =
211                         "Test creation of buckets";
212                 return AST_TEST_NOT_RUN;
213         case TEST_EXECUTE:
214                 break;
215         }
216
217         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
218                 ast_test_status_update(test, "Failed to allocate bucket\n");
219                 return AST_TEST_FAIL;
220         }
221
222         bucket_test_wizard_clear();
223
224         if (ast_bucket_create(bucket)) {
225                 ast_test_status_update(test, "Failed to create bucket with URI '%s'\n",
226                         ast_sorcery_object_get_id(bucket));
227                 return AST_TEST_FAIL;
228         }
229
230         if (!bucket_test_wizard_state.created) {
231                 ast_test_status_update(test, "Bucket creation returned success but scheme implementation never actually created it\n");
232                 return AST_TEST_FAIL;
233         }
234
235         if (!ast_bucket_create(bucket)) {
236                 ast_test_status_update(test, "Successfully created bucket with URI '%s' twice\n",
237                         ast_sorcery_object_get_id(bucket));
238                 return AST_TEST_FAIL;
239         }
240
241         return AST_TEST_PASS;
242 }
243
244 AST_TEST_DEFINE(bucket_clone)
245 {
246         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
247         RAII_VAR(struct ast_bucket *, clone, NULL, ao2_cleanup);
248
249         switch (cmd) {
250         case TEST_INIT:
251                 info->name = "bucket_clone";
252                 info->category = "/main/bucket/";
253                 info->summary = "bucket clone unit test";
254                 info->description =
255                         "Test cloning a bucket";
256                 return AST_TEST_NOT_RUN;
257         case TEST_EXECUTE:
258                 break;
259         }
260
261         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
262                 ast_test_status_update(test, "Failed to allocate bucket\n");
263                 return AST_TEST_FAIL;
264         }
265
266         bucket_test_wizard_clear();
267
268         if (ast_bucket_create(bucket)) {
269                 ast_test_status_update(test, "Failed to create bucket with URI '%s'\n",
270                         ast_sorcery_object_get_id(bucket));
271                 return AST_TEST_FAIL;
272         }
273
274         clone = ast_bucket_clone(bucket);
275         if (!clone) {
276                 ast_test_status_update(test, "Failed to clone bucket with URI '%s'\n",
277                         ast_sorcery_object_get_id(bucket));
278                 return AST_TEST_FAIL;
279         }
280
281         ast_test_validate(test, strcmp(ast_sorcery_object_get_id(bucket), ast_sorcery_object_get_id(clone)) == 0);
282         ast_test_validate(test, bucket->scheme_impl == clone->scheme_impl);
283         ast_test_validate(test, strcmp(bucket->scheme, clone->scheme) == 0);
284
285         return AST_TEST_PASS;
286 }
287
288 AST_TEST_DEFINE(bucket_delete)
289 {
290         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
291
292         switch (cmd) {
293         case TEST_INIT:
294                 info->name = "bucket_delete";
295                 info->category = "/main/bucket/";
296                 info->summary = "bucket deletion unit test";
297                 info->description =
298                         "Test deletion of buckets";
299                 return AST_TEST_NOT_RUN;
300         case TEST_EXECUTE:
301                 break;
302         }
303
304         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
305                 ast_test_status_update(test, "Failed to allocate bucket\n");
306                 return AST_TEST_FAIL;
307         }
308
309         bucket_test_wizard_clear();
310
311         if (ast_bucket_delete(bucket)) {
312                 ast_test_status_update(test, "Failed to delete bucket with URI '%s'\n",
313                         ast_sorcery_object_get_id(bucket));
314                 return AST_TEST_FAIL;
315         }
316
317         if (!bucket_test_wizard_state.deleted) {
318                 ast_test_status_update(test, "Bucket deletion returned success but scheme implementation never actually deleted it\n");
319                 return AST_TEST_FAIL;
320         }
321
322         if (!ast_bucket_delete(bucket)) {
323                 ast_test_status_update(test, "Successfully deleted bucket with URI '%s' twice\n",
324                         ast_sorcery_object_get_id(bucket));
325                 return AST_TEST_FAIL;
326         }
327
328         return AST_TEST_PASS;
329 }
330
331 AST_TEST_DEFINE(bucket_is_stale)
332 {
333         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
334
335         switch (cmd) {
336         case TEST_INIT:
337                 info->name = "bucket_is_stale";
338                 info->category = "/main/bucket/";
339                 info->summary = "bucket staleness unit test";
340                 info->description =
341                         "Test if staleness of a bucket is reported correctly";
342                 return AST_TEST_NOT_RUN;
343         case TEST_EXECUTE:
344                 break;
345         }
346
347         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
348                 ast_test_status_update(test, "Failed to allocate bucket\n");
349                 return AST_TEST_FAIL;
350         }
351
352         bucket_test_wizard_clear();
353
354         ast_test_validate(test, ast_bucket_is_stale(bucket) == 0);
355
356         bucket_test_wizard_state.is_stale = 1;
357
358         ast_test_validate(test, ast_bucket_is_stale(bucket) == 1);
359
360         return AST_TEST_PASS;
361 }
362
363 AST_TEST_DEFINE(bucket_json)
364 {
365         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
366         RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
367         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
368
369         switch (cmd) {
370         case TEST_INIT:
371                 info->name = "bucket_json";
372                 info->category = "/main/bucket/";
373                 info->summary = "bucket json unit test";
374                 info->description =
375                         "Test creation of JSON for a bucket";
376                 return AST_TEST_NOT_RUN;
377         case TEST_EXECUTE:
378                 break;
379         }
380
381         if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
382                 ast_test_status_update(test, "Failed to allocate bucket\n");
383                 return AST_TEST_FAIL;
384         }
385
386         ast_str_container_add(bucket->buckets, "test:///tmp/bob/joe");
387         ast_str_container_add(bucket->files, "test:///tmp/bob/recording.wav");
388
389         expected = ast_json_pack("{s: s, s: s, s: [s], s: s, s: [s], s: s}",
390                 "modified", "0.000000", "created", "0.000000",
391                 "buckets", "test:///tmp/bob/joe",
392                 "scheme", "test",
393                 "files", "test:///tmp/bob/recording.wav",
394                 "id", "test:///tmp/bob");
395         if (!expected) {
396                 ast_test_status_update(test, "Could not produce JSON for expected bucket value\n");
397                 return AST_TEST_FAIL;
398         }
399
400         json = ast_bucket_json(bucket);
401         if (!json) {
402                 ast_test_status_update(test, "Could not produce JSON for a valid bucket\n");
403                 return AST_TEST_FAIL;
404         }
405
406         if (!ast_json_equal(json, expected)) {
407                 ast_test_status_update(test, "Bucket JSON does not match expected output\n");
408                 return AST_TEST_FAIL;
409         }
410
411         return AST_TEST_PASS;
412 }
413
414 AST_TEST_DEFINE(bucket_retrieve)
415 {
416         RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
417
418         switch (cmd) {
419         case TEST_INIT:
420                 info->name = "bucket_retrieve";
421                 info->category = "/main/bucket/";
422                 info->summary = "bucket retrieval unit test";
423                 info->description =
424                         "Test retrieval of buckets";
425                 return AST_TEST_NOT_RUN;
426         case TEST_EXECUTE:
427                 break;
428         }
429
430         if (!(bucket = ast_bucket_retrieve("test://tmp/bob"))) {
431                 ast_test_status_update(test, "Failed to retrieve known valid bucket\n");
432                 return AST_TEST_FAIL;
433         }
434
435         return AST_TEST_PASS;
436 }
437
438 AST_TEST_DEFINE(bucket_file_alloc)
439 {
440         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
441
442         switch (cmd) {
443         case TEST_INIT:
444                 info->name = "bucket_file_alloc";
445                 info->category = "/main/bucket/";
446                 info->summary = "bucket file allocation unit test";
447                 info->description =
448                         "Test allocation of bucket files";
449                 return AST_TEST_NOT_RUN;
450         case TEST_EXECUTE:
451                 break;
452         }
453
454         if ((file = ast_bucket_file_alloc(""))) {
455                 ast_test_status_update(test, "Allocated a file with no URI provided\n");
456                 return AST_TEST_FAIL;
457         }
458
459         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
460                 ast_test_status_update(test, "Failed to allocate file\n");
461                 return AST_TEST_FAIL;
462         }
463
464         if (ast_strlen_zero(file->path)) {
465                 ast_test_status_update(test, "Expected temporary path in allocated file");
466                 return AST_TEST_FAIL;
467         }
468
469         if (strcmp(ast_sorcery_object_get_id(file), "test:///tmp/bob")) {
470                 ast_test_status_update(test, "URI within allocated file is '%s' and should be test:///tmp/bob\n",
471                         ast_sorcery_object_get_id(file));
472                 return AST_TEST_FAIL;
473         }
474
475         if (strcmp(file->scheme, "test")) {
476                 ast_test_status_update(test, "Scheme within allocated file is '%s' and should be test\n",
477                         file->scheme);
478                 return AST_TEST_FAIL;
479         }
480
481         return AST_TEST_PASS;
482 }
483
484 AST_TEST_DEFINE(bucket_file_create)
485 {
486         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
487
488         switch (cmd) {
489         case TEST_INIT:
490                 info->name = "bucket_file_create";
491                 info->category = "/main/bucket/";
492                 info->summary = "file creation unit test";
493                 info->description =
494                         "Test creation of files";
495                 return AST_TEST_NOT_RUN;
496         case TEST_EXECUTE:
497                 break;
498         }
499
500         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
501                 ast_test_status_update(test, "Failed to allocate file\n");
502                 return AST_TEST_FAIL;
503         }
504
505         bucket_test_wizard_clear();
506
507         if (ast_bucket_file_create(file)) {
508                 ast_test_status_update(test, "Failed to create file with URI '%s'\n",
509                         ast_sorcery_object_get_id(file));
510                 return AST_TEST_FAIL;
511         }
512
513         if (!bucket_test_wizard_state.created) {
514                 ast_test_status_update(test, "Bucket file creation returned success but scheme implementation never actually created it\n");
515                 return AST_TEST_FAIL;
516         }
517
518         if (!ast_bucket_file_create(file)) {
519                 ast_test_status_update(test, "Successfully created file with URI '%s' twice\n",
520                         ast_sorcery_object_get_id(file));
521                 return AST_TEST_FAIL;
522         }
523
524         return AST_TEST_PASS;
525 }
526
527 AST_TEST_DEFINE(bucket_file_clone)
528 {
529         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
530         RAII_VAR(struct ast_bucket_file *, clone, NULL, ao2_cleanup);
531
532         switch (cmd) {
533         case TEST_INIT:
534                 info->name = "bucket_file_clone";
535                 info->category = "/main/bucket/";
536                 info->summary = "file clone unit test";
537                 info->description =
538                         "Test cloning a file";
539                 return AST_TEST_NOT_RUN;
540         case TEST_EXECUTE:
541                 break;
542         }
543
544         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
545                 ast_test_status_update(test, "Failed to allocate file\n");
546                 return AST_TEST_FAIL;
547         }
548
549         bucket_test_wizard_clear();
550
551         if (ast_bucket_file_create(file)) {
552                 ast_test_status_update(test, "Failed to create file with URI '%s'\n",
553                         ast_sorcery_object_get_id(file));
554                 return AST_TEST_FAIL;
555         }
556         ast_bucket_file_metadata_set(file, "bob", "joe");
557
558         clone = ast_bucket_file_clone(file);
559         if (!clone) {
560                 ast_test_status_update(test, "Failed to clone file with URI '%s'\n",
561                         ast_sorcery_object_get_id(file));
562                 return AST_TEST_FAIL;
563         }
564
565         ast_test_validate(test, strcmp(ast_sorcery_object_get_id(file), ast_sorcery_object_get_id(clone)) == 0);
566         ast_test_validate(test, file->scheme_impl == clone->scheme_impl);
567         ast_test_validate(test, strcmp(file->scheme, clone->scheme) == 0);
568         ast_test_validate(test, ao2_container_count(file->metadata) == ao2_container_count(clone->metadata));
569
570         return AST_TEST_PASS;
571 }
572
573 AST_TEST_DEFINE(bucket_file_copy)
574 {
575         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
576         RAII_VAR(struct ast_bucket_file *, copy, NULL, ao2_cleanup);
577         FILE *temporary;
578         struct stat old, new;
579         RAII_VAR(struct ast_bucket_metadata *, metadata, NULL, ao2_cleanup);
580
581         switch (cmd) {
582         case TEST_INIT:
583                 info->name = "bucket_file_copy";
584                 info->category = "/main/bucket/";
585                 info->summary = "bucket file copying unit test";
586                 info->description =
587                         "Test copying of bucket files";
588                 return AST_TEST_NOT_RUN;
589         case TEST_EXECUTE:
590                 break;
591         }
592
593         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
594                 ast_test_status_update(test, "Failed to allocate file\n");
595                 return AST_TEST_FAIL;
596         }
597
598         ast_bucket_file_metadata_set(file, "bob", "joe");
599
600         if (!(temporary = fopen(file->path, "w"))) {
601                 ast_test_status_update(test, "Failed to open temporary file '%s'\n", file->path);
602                 return AST_TEST_FAIL;
603         }
604
605         fprintf(temporary, "bob");
606         fclose(temporary);
607
608         if (!(copy = ast_bucket_file_copy(file, "test:///tmp/bob2"))) {
609                 ast_test_status_update(test, "Failed to copy file '%s' to test:///tmp/bob2\n",
610                         ast_sorcery_object_get_id(file));
611                 return AST_TEST_FAIL;
612         }
613
614         if (stat(file->path, &old)) {
615                 ast_test_status_update(test, "Failed to retrieve information on old file '%s'\n", file->path);
616                 return AST_TEST_FAIL;
617         }
618
619         if (stat(copy->path, &new)) {
620                 ast_test_status_update(test, "Failed to retrieve information on copy file '%s'\n", copy->path);
621                 return AST_TEST_FAIL;
622         }
623
624         if (old.st_size != new.st_size) {
625                 ast_test_status_update(test, "Copying of underlying temporary file failed\n");
626                 return AST_TEST_FAIL;
627         }
628
629         if (ao2_container_count(file->metadata) != ao2_container_count(copy->metadata)) {
630                 ast_test_status_update(test, "Number of metadata entries does not match original\n");
631                 return AST_TEST_FAIL;
632         }
633
634         metadata = ast_bucket_file_metadata_get(copy, "bob");
635         if (!metadata) {
636                 ast_test_status_update(test, "Copy of file does not have expected metadata\n");
637                 return AST_TEST_FAIL;
638         }
639
640         if (strcmp(metadata->value, "joe")) {
641                 ast_test_status_update(test, "Copy of file contains metadata for 'bob' but value is not what it should be\n");
642                 return AST_TEST_FAIL;
643         }
644
645         return AST_TEST_PASS;
646 }
647
648 AST_TEST_DEFINE(bucket_file_retrieve)
649 {
650         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
651
652         switch (cmd) {
653         case TEST_INIT:
654                 info->name = "bucket_file_retrieve";
655                 info->category = "/main/bucket/";
656                 info->summary = "file retrieval unit test";
657                 info->description =
658                         "Test retrieval of files";
659                 return AST_TEST_NOT_RUN;
660         case TEST_EXECUTE:
661                 break;
662         }
663
664         if (!(file = ast_bucket_file_retrieve("test://tmp/bob"))) {
665                 ast_test_status_update(test, "Failed to retrieve known valid file\n");
666                 return AST_TEST_FAIL;
667         }
668
669         return AST_TEST_PASS;
670 }
671
672 AST_TEST_DEFINE(bucket_file_update)
673 {
674         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
675
676         switch (cmd) {
677         case TEST_INIT:
678                 info->name = "bucket_file_update";
679                 info->category = "/main/bucket/";
680                 info->summary = "file updating unit test";
681                 info->description =
682                         "Test updating of files";
683                 return AST_TEST_NOT_RUN;
684         case TEST_EXECUTE:
685                 break;
686         }
687
688         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
689                 ast_test_status_update(test, "Failed to allocate file\n");
690                 return AST_TEST_FAIL;
691         }
692
693         bucket_test_wizard_clear();
694
695         if (ast_bucket_file_update(file)) {
696                 ast_test_status_update(test, "Failed to update file with URI '%s'\n",
697                         ast_sorcery_object_get_id(file));
698                 return AST_TEST_FAIL;
699         }
700
701         if (!bucket_test_wizard_state.updated) {
702                 ast_test_status_update(test, "Successfully returned file was updated, but it was not\n");
703                 return AST_TEST_FAIL;
704         }
705
706         if (!ast_bucket_file_update(file)) {
707                 ast_test_status_update(test, "Successfully updated file with URI '%s' twice\n",
708                         ast_sorcery_object_get_id(file));
709                 return AST_TEST_FAIL;
710         }
711
712         return AST_TEST_PASS;
713 }
714
715 AST_TEST_DEFINE(bucket_file_delete)
716 {
717         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
718
719         switch (cmd) {
720         case TEST_INIT:
721                 info->name = "bucket_file_delete";
722                 info->category = "/main/bucket/";
723                 info->summary = "file deletion unit test";
724                 info->description =
725                         "Test deletion of files";
726                 return AST_TEST_NOT_RUN;
727         case TEST_EXECUTE:
728                 break;
729         }
730
731         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
732                 ast_test_status_update(test, "Failed to allocate file\n");
733                 return AST_TEST_FAIL;
734         }
735
736         bucket_test_wizard_clear();
737
738         if (ast_bucket_file_delete(file)) {
739                 ast_test_status_update(test, "Failed to delete file with URI '%s'\n",
740                         ast_sorcery_object_get_id(file));
741                 return AST_TEST_FAIL;
742         }
743
744         if (!bucket_test_wizard_state.deleted) {
745                 ast_test_status_update(test, "Bucket file deletion returned success but scheme implementation never actually deleted it\n");
746                 return AST_TEST_FAIL;
747         }
748
749         if (!ast_bucket_file_delete(file)) {
750                 ast_test_status_update(test, "Successfully deleted file with URI '%s' twice\n",
751                         ast_sorcery_object_get_id(file));
752                 return AST_TEST_FAIL;
753         }
754
755         return AST_TEST_PASS;
756 }
757
758 AST_TEST_DEFINE(bucket_file_is_stale)
759 {
760         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
761
762         switch (cmd) {
763         case TEST_INIT:
764                 info->name = "bucket_file_is_stale";
765                 info->category = "/main/bucket/";
766                 info->summary = "file staleness unit test";
767                 info->description =
768                         "Test if staleness of a bucket file is reported correctly";
769                 return AST_TEST_NOT_RUN;
770         case TEST_EXECUTE:
771                 break;
772         }
773
774         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
775                 ast_test_status_update(test, "Failed to allocate file\n");
776                 return AST_TEST_FAIL;
777         }
778
779         bucket_test_wizard_clear();
780
781         ast_test_validate(test, ast_bucket_file_is_stale(file) == 0);
782
783         bucket_test_wizard_state.is_stale = 1;
784
785         ast_test_validate(test, ast_bucket_file_is_stale(file) == 1);
786
787         return AST_TEST_PASS;
788 }
789
790 AST_TEST_DEFINE(bucket_file_metadata_set)
791 {
792         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
793         RAII_VAR(struct ast_bucket_metadata *, metadata, NULL, ao2_cleanup);
794
795         switch (cmd) {
796         case TEST_INIT:
797                 info->name = "bucket_file_metadata_set";
798                 info->category = "/main/bucket/";
799                 info->summary = "file metadata setting unit test";
800                 info->description =
801                         "Test setting of metadata on files";
802                 return AST_TEST_NOT_RUN;
803         case TEST_EXECUTE:
804                 break;
805         }
806
807         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
808                 ast_test_status_update(test, "Failed to allocate file\n");
809                 return AST_TEST_FAIL;
810         }
811
812         if (ao2_container_count(file->metadata) != 0) {
813                 ast_test_status_update(test, "Newly allocated file has metadata count of '%d' when should be 0\n",
814                         ao2_container_count(file->metadata));
815                 return AST_TEST_FAIL;
816         }
817
818         if (ast_bucket_file_metadata_set(file, "bob", "joe")) {
819                 ast_test_status_update(test, "Failed to set metadata 'bob' to 'joe' on newly allocated file\n");
820                 return AST_TEST_FAIL;
821         }
822
823         if (!(metadata = ao2_find(file->metadata, "bob", OBJ_KEY))) {
824                 ast_test_status_update(test, "Failed to find set metadata 'bob' on newly allocated file\n");
825                 return AST_TEST_FAIL;
826         }
827
828         if (strcmp(metadata->value, "joe")) {
829                 ast_test_status_update(test, "Metadata has value '%s' when should be 'joe'\n",
830                         metadata->value);
831                 return AST_TEST_FAIL;
832         }
833
834         ao2_cleanup(metadata);
835         metadata = NULL;
836
837         if (ast_bucket_file_metadata_set(file, "bob", "fred")) {
838                 ast_test_status_update(test, "Failed to overwrite metadata 'bob' with new value 'fred'\n");
839                 return AST_TEST_FAIL;
840         }
841
842         if (!(metadata = ao2_find(file->metadata, "bob", OBJ_KEY))) {
843                 ast_test_status_update(test, "Failed to find overwritten metadata 'bob' on newly allocated file\n");
844                 return AST_TEST_FAIL;
845         }
846
847         if (strcmp(metadata->value, "fred")) {
848                 ast_test_status_update(test, "Metadata has value '%s' when should be 'fred'\n",
849                         metadata->value);
850                 return AST_TEST_FAIL;
851         }
852
853         return AST_TEST_PASS;
854 }
855
856 AST_TEST_DEFINE(bucket_file_metadata_unset)
857 {
858         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
859         RAII_VAR(struct ast_bucket_metadata *, metadata, NULL, ao2_cleanup);
860
861         switch (cmd) {
862         case TEST_INIT:
863                 info->name = "bucket_file_metadata_unset";
864                 info->category = "/main/bucket/";
865                 info->summary = "file metadata unsetting unit test";
866                 info->description =
867                         "Test unsetting of metadata on files";
868                 return AST_TEST_NOT_RUN;
869         case TEST_EXECUTE:
870                 break;
871         }
872
873         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
874                 ast_test_status_update(test, "Failed to allocate file\n");
875                 return AST_TEST_FAIL;
876         }
877
878         if (ast_bucket_file_metadata_set(file, "bob", "joe")) {
879                 ast_test_status_update(test, "Failed to set metadata 'bob' to 'joe' on newly allocated file\n");
880                 return AST_TEST_FAIL;
881         }
882
883         if (ast_bucket_file_metadata_unset(file, "bob")) {
884                 ast_test_status_update(test, "Failed to unset metadata 'bob' on newly allocated file\n");
885                 return AST_TEST_FAIL;
886         }
887
888         if ((metadata = ao2_find(file->metadata, "bob", OBJ_KEY))) {
889                 ast_test_status_update(test, "Metadata 'bob' was unset, but can still be found\n");
890                 return AST_TEST_FAIL;
891         }
892
893         return AST_TEST_PASS;
894 }
895
896 AST_TEST_DEFINE(bucket_file_metadata_get)
897 {
898         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
899         RAII_VAR(struct ast_bucket_metadata *, metadata, NULL, ao2_cleanup);
900
901         switch (cmd) {
902         case TEST_INIT:
903                 info->name = "bucket_file_metadata_get";
904                 info->category = "/main/bucket/";
905                 info->summary = "file metadata getting unit test";
906                 info->description =
907                         "Test getting of metadata on files";
908                 return AST_TEST_NOT_RUN;
909         case TEST_EXECUTE:
910                 break;
911         }
912
913         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
914                 ast_test_status_update(test, "Failed to allocate file\n");
915                 return AST_TEST_FAIL;
916         }
917
918         if (ast_bucket_file_metadata_set(file, "bob", "joe")) {
919                 ast_test_status_update(test, "Failed to set metadata 'bob' to 'joe' on newly allocated file\n");
920                 return AST_TEST_FAIL;
921         }
922
923         if (!(metadata = ast_bucket_file_metadata_get(file, "bob"))) {
924                 ast_test_status_update(test, "Failed to retrieve metadata 'bob' that was just set\n");
925                 return AST_TEST_FAIL;
926         }
927
928         if (strcmp(metadata->value, "joe")) {
929                 ast_test_status_update(test, "Retrieved metadata value is '%s' while it should be 'joe'\n",
930                         metadata->value);
931                 return AST_TEST_FAIL;
932         }
933
934         return AST_TEST_PASS;
935 }
936
937 AST_TEST_DEFINE(bucket_file_json)
938 {
939         RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
940         RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
941         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
942
943         switch (cmd) {
944         case TEST_INIT:
945                 info->name = "bucket_file_json";
946                 info->category = "/main/bucket/";
947                 info->summary = "file json unit test";
948                 info->description =
949                         "Test creation of JSON for a file";
950                 return AST_TEST_NOT_RUN;
951         case TEST_EXECUTE:
952                 break;
953         }
954
955         if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
956                 ast_test_status_update(test, "Failed to allocate bucket\n");
957                 return AST_TEST_FAIL;
958         }
959
960         if (ast_bucket_file_metadata_set(file, "bob", "joe")) {
961                 ast_test_status_update(test, "Failed to set metadata 'bob' to 'joe' on newly allocated file\n");
962                 return AST_TEST_FAIL;
963         }
964
965         expected = ast_json_pack("{s: s, s: s, s: s, s: s, s: {s :s}}",
966                 "modified", "0.000000", "created", "0.000000", "scheme", "test",
967                 "id", "test:///tmp/bob", "metadata", "bob", "joe");
968         if (!expected) {
969                 ast_test_status_update(test, "Could not produce JSON for expected bucket file value\n");
970                 return AST_TEST_FAIL;
971         }
972
973         json = ast_bucket_file_json(file);
974         if (!json) {
975                 ast_test_status_update(test, "Could not produce JSON for a valid file\n");
976                 return AST_TEST_FAIL;
977         }
978
979         if (!ast_json_equal(json, expected)) {
980                 ast_test_status_update(test, "Bucket file JSON does not match expected output\n");
981                 return AST_TEST_FAIL;
982         }
983
984         return AST_TEST_PASS;
985 }
986
987 static int unload_module(void)
988 {
989         AST_TEST_UNREGISTER(bucket_scheme_register);
990         AST_TEST_UNREGISTER(bucket_alloc);
991         AST_TEST_UNREGISTER(bucket_create);
992         AST_TEST_UNREGISTER(bucket_clone);
993         AST_TEST_UNREGISTER(bucket_delete);
994         AST_TEST_UNREGISTER(bucket_retrieve);
995         AST_TEST_UNREGISTER(bucket_json);
996         AST_TEST_UNREGISTER(bucket_file_alloc);
997         AST_TEST_UNREGISTER(bucket_file_create);
998         AST_TEST_UNREGISTER(bucket_file_clone);
999         AST_TEST_UNREGISTER(bucket_file_copy);
1000         AST_TEST_UNREGISTER(bucket_file_retrieve);
1001         AST_TEST_UNREGISTER(bucket_file_update);
1002         AST_TEST_UNREGISTER(bucket_file_delete);
1003         AST_TEST_UNREGISTER(bucket_file_metadata_set);
1004         AST_TEST_UNREGISTER(bucket_file_metadata_unset);
1005         AST_TEST_UNREGISTER(bucket_file_metadata_get);
1006         AST_TEST_UNREGISTER(bucket_file_json);
1007         return 0;
1008 }
1009
1010 static int load_module(void)
1011 {
1012         if (ast_bucket_scheme_register("test", &bucket_test_wizard, &bucket_file_test_wizard,
1013                 ast_bucket_file_temporary_create, ast_bucket_file_temporary_destroy)) {
1014                 ast_log(LOG_ERROR, "Failed to register Bucket test wizard scheme implementation\n");
1015                 return AST_MODULE_LOAD_FAILURE;
1016         }
1017
1018         AST_TEST_REGISTER(bucket_scheme_register);
1019         AST_TEST_REGISTER(bucket_alloc);
1020         AST_TEST_REGISTER(bucket_create);
1021         AST_TEST_REGISTER(bucket_clone);
1022         AST_TEST_REGISTER(bucket_delete);
1023         AST_TEST_REGISTER(bucket_retrieve);
1024         AST_TEST_REGISTER(bucket_is_stale);
1025         AST_TEST_REGISTER(bucket_json);
1026         AST_TEST_REGISTER(bucket_file_alloc);
1027         AST_TEST_REGISTER(bucket_file_create);
1028         AST_TEST_REGISTER(bucket_file_clone);
1029         AST_TEST_REGISTER(bucket_file_copy);
1030         AST_TEST_REGISTER(bucket_file_retrieve);
1031         AST_TEST_REGISTER(bucket_file_update);
1032         AST_TEST_REGISTER(bucket_file_delete);
1033         AST_TEST_REGISTER(bucket_file_is_stale);
1034         AST_TEST_REGISTER(bucket_file_metadata_set);
1035         AST_TEST_REGISTER(bucket_file_metadata_unset);
1036         AST_TEST_REGISTER(bucket_file_metadata_get);
1037         AST_TEST_REGISTER(bucket_file_json);
1038         return AST_MODULE_LOAD_SUCCESS;
1039 }
1040
1041 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Bucket test module");