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