| @@ -182,10 +182,37 @@ | |||
| "special": "iter is unique" | |||
| }, | |||
| { | |||
| "skip": 1, | |||
| "title": "dump is correct", | |||
| "cmd": [ "dump" ], | |||
| "exit": 0, | |||
| "stdout": "ERROR: tag needs to start with a \"+\" (add) or a \"-\" (remove).\n" | |||
| "stdout_re": "{.*filename.*newfile.txt.*hashes.*90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c.*size.*19.*type.*file.*}\n{.*foo.*bar=baz.*hashes.*90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c.*type.*metadata.*}\n{.*filename.*test.txt.*90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c.*size.*19.*type.*file.*}\n{.*filename.*newfile.txt.*90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c.*size.*19.*type.*file.*}\n{.*filename.*newfile.txt.*90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c.*size.*19.*type.*file.*}\n" | |||
| }, | |||
| { | |||
| "title": "that import can be done", | |||
| "cmd": [ "import" ], | |||
| "stdin": "{\"created_by_ref\": \"e7ad5ea1-1203-4951-9dca-ec852a7b8166\", \"foo\": [\"bar=baz\"], \"hashes\": [\"sha512:90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c\"], \"modified\": \"2022-08-21T00:13:51.245871Z\", \"sig\": \"g7k4plXjzz9y8YwJM2ncCIxaqlBpdbITPvKlDtfO7LSFmbZ-qcj0M0lN9h8twNU-n163dNsDGmQA4_s8pJB0liBHDwkjpYQvxfeztQDWNaVN7Xnh2MOj-wBzUbLTVnsJULXwVQrUjngzWjjGQ3jy6gwA\", \"tag\": [\"\"], \"type\": \"metadata\", \"uuid\": \"25ec10e6-c3d0-4363-a762-899dade7f93c\"}\n{\"created_by_ref\": \"2de7a389-410c-4755-8c62-f3254c7e971e\", \"dir\": \"\", \"filename\": \"FreeBSD-14.0-CURRENT-arm64-aarch64-ROCK64-20220331-d53927b0bae-254105.img.xz\", \"hashes\": [\"sha512:3eba2aa09a79fd7adec32ace93c6725e75b91a55192b5bfa827b893fae1c2cdbc040e282138387797f245c1f27da5e8ff7a02f59ff75c73b3d999d1e0a8752ad\"], \"id\": \"d9e8fc1a-9794-5e70-b67b-11d708ec8947\", \"modified\": \"2022-08-02T07:52:50.216428Z\", \"mtime\": \"2022-03-31T10:15:14.000000Z\", \"sig\": \"F3Ch1BQB57RAgFNKNF5btCRZS3jFmafhvcdoWuu6WHo5JzyhuQ7jNvZkv4tzgWqlMfPecLJMMmSAMcwqorykDWi854ZfKUm3F-JOTk8R7qRKdHd23rwVGDEXtVHv-kynkh8WemPsfc2V1HT8JKG9NhwA\", \"size\": 551750948, \"type\": \"file\", \"uuid\": \"89544934-6ed9-46ca-b8d0-77c5209f798d\"}\n" | |||
| }, | |||
| { | |||
| "special": "verify store object cnt", | |||
| "comment": "and the objects were imported", | |||
| "count": 7 | |||
| }, | |||
| { | |||
| "title": "than an object can be dropped", | |||
| "cmd": [ "drop", "25ec10e6-c3d0-4363-a762-899dade7f93c" ] | |||
| }, | |||
| { | |||
| "special": "verify store object cnt", | |||
| "comment": "and the object was dropped", | |||
| "count": 6 | |||
| }, | |||
| { | |||
| "title": "than an object can be dropped", | |||
| "cmd": [ "drop", "89544934-6ed9-46ca-b8d0-77c5209f798d" ] | |||
| }, | |||
| { | |||
| "special": "verify store object cnt", | |||
| "comment": "and the object was dropped", | |||
| "count": 5 | |||
| } | |||
| ] | |||
| @@ -529,6 +529,20 @@ class ObjectStore(object): | |||
| return obj | |||
| def drop_uuid(self, uuid): | |||
| uuid = _makeuuid(uuid) | |||
| obj = self.by_id(uuid) | |||
| del self._uuids[uuid] | |||
| if obj.type == 'file': | |||
| del self._uuids[obj.id] | |||
| for j in obj.hashes: | |||
| h = self.makehash(j) | |||
| self._hashes[h].remove(obj) | |||
| def by_id(self, id): | |||
| '''Look up an object by it's UUID.''' | |||
| @@ -772,7 +786,7 @@ def cmd_dump(options): | |||
| persona, objstr = get_objstore(options) | |||
| for i in objstr: | |||
| print(repr(i)) | |||
| print(i.encode('json')) | |||
| def cmd_list(options): | |||
| persona, objstr = get_objstore(options) | |||
| @@ -807,6 +821,33 @@ def cmd_list(options): | |||
| write_objstore(options, objstr) | |||
| def cmd_import(options): | |||
| persona, objstr = get_objstore(options) | |||
| jd = json.JSONDecoder() | |||
| inp = sys.stdin.read() | |||
| while inp: | |||
| inp = inp.strip() | |||
| jobj, endpos = jd.raw_decode(inp) | |||
| obj = MDBase.create_obj(jobj) | |||
| objstr.loadobj(obj) | |||
| inp = inp[endpos:] | |||
| write_objstore(options, objstr) | |||
| def cmd_drop(options): | |||
| persona, objstr = get_objstore(options) | |||
| for i in options.uuids: | |||
| objstr.drop_uuid(i) | |||
| write_objstore(options, objstr) | |||
| def main(): | |||
| import argparse | |||
| @@ -847,6 +888,14 @@ def main(): | |||
| parser_dump = subparsers.add_parser('dump', help='dump all the objects') | |||
| parser_dump.set_defaults(func=cmd_dump) | |||
| parser_import = subparsers.add_parser('import', help='import objects encoded as json') | |||
| parser_import.set_defaults(func=cmd_import) | |||
| parser_drop = subparsers.add_parser('drop', help='drop the object specified by UUID') | |||
| parser_drop.add_argument('uuids', nargs='+', | |||
| help='UUID of object to drop') | |||
| parser_drop.set_defaults(func=cmd_drop) | |||
| options = parser.parse_args() | |||
| fun = options.func | |||
| @@ -1349,10 +1398,19 @@ class _TestCases(unittest.TestCase): | |||
| with self.subTest(file=f, title=cmd['title']), \ | |||
| mock.patch('os.path.expanduser', | |||
| side_effect=expandusermock) as eu, \ | |||
| mock.patch('sys.stdin', io.StringIO()) as stdin, \ | |||
| mock.patch('sys.stdout', io.StringIO()) as stdout, \ | |||
| mock.patch('sys.stderr', io.StringIO()) as stderr, \ | |||
| mock.patch('sys.argv', [ 'progname', ] + | |||
| cmd['cmd']) as argv: | |||
| # if there is stdin | |||
| test_stdin = cmd.get('stdin', '') | |||
| # provide it | |||
| stdin.write(test_stdin) | |||
| stdin.seek(0) | |||
| with self.assertRaises(SystemExit) as cm: | |||
| main() | |||