diff --git a/tests/test_delete.py b/tests/test_delete.py new file mode 100644 index 0000000..345c216 --- /dev/null +++ b/tests/test_delete.py @@ -0,0 +1,61 @@ +# Copyright 2023 (c) Anna Schumaker. +"""Tests the `xfstestsdb delete` command.""" +import errno +import io +import unittest +import unittest.mock +import xfstestsdb.delete + + +class TestDelete(unittest.TestCase): + """Tests the `xfstestsdb delete` command.""" + + def setUp(self): + """Set up common variables.""" + self.xfstestsdb = xfstestsdb.Command() + self.delete = self.xfstestsdb.commands["delete"] + + def test_init(self): + """Check that the delete command was set up properly.""" + self.assertIsInstance(self.delete, xfstestsdb.commands.Command) + self.assertIsInstance(self.delete, xfstestsdb.delete.Command) + self.assertEqual(self.xfstestsdb.subparser.choices["delete"], + self.delete.parser) + + @unittest.mock.patch("sys.stdout", new_callable=io.StringIO) + def test_delete(self, mock_stdout: io.StringIO): + """Test the `xfstestsdb delete` command with valid input.""" + self.xfstestsdb.run(["new", "/dev/test"]) + self.xfstestsdb.run(["delete", "1"]) + self.assertRegex(mock_stdout.getvalue(), "run #1 has been deleted") + + cur = self.xfstestsdb.sql("SELECT COUNT(*) FROM xfstests_runs") + self.assertEqual(cur.fetchone()["COUNT(*)"], 0) + + @unittest.mock.patch("sys.stdout", new_callable=io.StringIO) + def test_delete_tags(self, mock_stdout: io.StringIO): + """Test the `xfstestsdb delete` command on a tagged run.""" + self.xfstestsdb.run(["new", "/dev/test"]) + self.xfstestsdb.run(["tag", "1", "mytag"]) + self.xfstestsdb.run(["delete", "1"]) + + cur = self.xfstestsdb.sql("SELECT COUNT(*) FROM tags") + self.assertEqual(cur.fetchone()["COUNT(*)"], 0) + + @unittest.mock.patch("sys.stderr", new_callable=io.StringIO) + def test_delete_error(self, mock_stderr: io.StringIO): + """Test the `xfstestsdb delete` command with invalid input.""" + with self.assertRaises(SystemExit): + self.xfstestsdb.run(["delete"]) + self.assertRegex(mock_stderr.getvalue(), + "error: the following arguments are required: runid") + + @unittest.mock.patch("sys.stderr", new_callable=io.StringIO) + def test_delete_enoent(self, mock_stderr: io.StringIO): + """Test the `xfstestsdb delete` command with an invalid runid.""" + with self.assertRaises(SystemExit) as sys_exit: + self.xfstestsdb.run(["delete", "2"]) + + self.assertEqual(sys_exit.exception.code, errno.ENOENT) + self.assertRegex(mock_stderr.getvalue(), + "error: run #2 does not exist") diff --git a/xfstestsdb/__init__.py b/xfstestsdb/__init__.py index 3453fe1..22e0587 100644 --- a/xfstestsdb/__init__.py +++ b/xfstestsdb/__init__.py @@ -2,6 +2,7 @@ """Implements the toplevel xfstestsdb command.""" import argparse from . import sqlite +from . import delete from . import new from . import rename from . import tag @@ -22,7 +23,8 @@ class Command: help="show version number and exit") self.subparser = self.parser.add_subparsers(title="commands") self.sql = sqlite.Connection() - self.commands = {"new": new.Command(self.subparser, self.sql), + self.commands = {"delete": delete.Command(self.subparser, self.sql), + "new": new.Command(self.subparser, self.sql), "rename": rename.Command(self.subparser, self.sql), "tag": tag.Command(self.subparser, self.sql), "untag": untag.Command(self.subparser, self.sql)} diff --git a/xfstestsdb/delete.py b/xfstestsdb/delete.py new file mode 100644 index 0000000..a70ee0f --- /dev/null +++ b/xfstestsdb/delete.py @@ -0,0 +1,29 @@ +# Copyright 2023 (c) Anna Schumaker. +"""The `xfstestsdb delete` command.""" +import argparse +import errno +import sys +from . import commands +from . import sqlite + + +class Command(commands.Command): + """The `xfstestsdb delete` command.""" + + def __init__(self, subparser: argparse.Action, + sql: sqlite.Connection) -> None: + """Set up the delete command.""" + super().__init__(subparser, sql, "delete", + help="delete an xfstests run") + self.parser.add_argument("runid", metavar="runid", nargs=1, type=int, + help="runid of the xfstests run to delete") + + def do_command(self, args: argparse.Namespace) -> None: + """Delete a row from the xfstests_runs table.""" + cur = self.sql("DELETE FROM xfstests_runs WHERE rowid=?", + args.runid[0]) + if cur.rowcount == 0: + print(f"error: run #{args.runid[0]} does not exist", + file=sys.stderr) + sys.exit(errno.ENOENT) + print(f"run #{args.runid[0]} has been deleted") diff --git a/xfstestsdb/xfstestsdb.sql b/xfstestsdb/xfstestsdb.sql index 06164c1..fc03e38 100644 --- a/xfstestsdb/xfstestsdb.sql +++ b/xfstestsdb/xfstestsdb.sql @@ -28,4 +28,6 @@ CREATE TABLE tags ( tag TEXT NOT NULL, PRIMARY KEY (runid, tag), FOREIGN KEY (runid) REFERENCES xfstests_runs (runid) + ON DELETE CASCADE + ON UPDATE CASCADE );