Repository: getmoto/moto

Issue:
Issue when calling delete_database() function in mock_glue decorator
# Issue

Hello, I am having an issue when using mock_glue decorator, unittest, and pytest. I am setting up a fake db, but when I try and tear it down, I get an error: `NotImplementedError: The delete_database action has not been implemented`. Could someone please help me out?

NOTE: This is running on the most recent version of moto.

# Code

```python
@mock_glue
class TestClickCommands(unittest.TestCase):

    def setUp(self):
        glue = boto3.client('glue', 'us-east-1')
        database_name = "db34"
        helpers.create_database(glue, database_name)

        table_names = ["table1", "table2", "table3"]
        table_inputs = {}

        for table_name in table_names:
            table_input = helpers.create_table_input(database_name, table_name)
            table_inputs[table_name] = table_input
            helpers.create_table(glue, database_name, table_name, table_input)    

    def tearDown(self):
        glue = boto3.client('glue', 'us-east-1')
        glue.batch_delete_table(DatabaseName='db34', TablesToDelete=["table1", "table2", "table3"])
        glue.delete_database(Name='db34')

    def test_cli(self):
        runner = CliRunner()
        result = runner.invoke(cli, ['-v'])
        self.assertEqual(result.exit_code, 0)

        result = runner.invoke(cli, ['-r'])
        self.assertEqual(result.exit_code, 0)

        result = runner.invoke(cli, ['-c', 'staging'])
        self.assertEqual(result.exit_code, 0)

        result = runner.invoke(cli, ['setup'])
        result = runner.invoke(cli, ['-c', 'staging'])
        self.assertEqual(result.exit_code, 0)
    
    def test_list_databases(self):
        runner = CliRunner()
        result = runner.invoke(cli, ['list-databases', '-r', 'us-east-1'])
        print(result.output)
        print(result.exception)
        self.assertEqual(result.exit_code, 0)

```

# Stack Trace
```python
_________________TestClickCommands.test_cli _______________________

self = <tests.mainfile_tests.TestClickCommands testMethod=test_cli>

    def tearDown(self):
        # s3 = boto3.resource("s3")
        # bucket = s3.Bucket("data-lake-staging-us-east-1")
        # for key in bucket.objects.all():
        #     key.delete()
        # bucket.delete()
    
        glue = boto3.client('glue', 'us-east-1')
        glue.batch_delete_table(DatabaseName='db34', TablesToDelete=["table1", "table2", "table3"])
    
        session = boto3.Session()
        glue_client = session.client('glue', 'us-east-1')
>       glue_client.delete_database(Name='db34')

tests/mainfile_tests.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.9/site-packages/botocore/client.py:357: in _api_call
    return self._make_api_call(operation_name, kwargs)
/usr/local/lib/python3.9/site-packages/botocore/client.py:662: in _make_api_call
    http, parsed_response = self._make_request(
/usr/local/lib/python3.9/site-packages/botocore/client.py:682: in _make_request
    return self._endpoint.make_request(operation_model, request_dict)
/usr/local/lib/python3.9/site-packages/botocore/endpoint.py:102: in make_request
    return self._send_request(request_dict, operation_model)
/usr/local/lib/python3.9/site-packages/botocore/endpoint.py:136: in _send_request
    while self._needs_retry(attempts, operation_model, request_dict,
/usr/local/lib/python3.9/site-packages/botocore/endpoint.py:253: in _needs_retry
    responses = self._event_emitter.emit(
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:356: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:228: in emit
    return self._emit(event_name, kwargs)
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:211: in _emit
    response = handler(**kwargs)
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:183: in __call__
    if self._checker(attempts, response, caught_exception):
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:250: in __call__
    should_retry = self._should_retry(attempt_number, response,
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:269: in _should_retry
    return self._checker(attempt_number, response, caught_exception)
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:316: in __call__
    checker_response = checker(attempt_number, response,
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:222: in __call__
    return self._check_caught_exception(
/usr/local/lib/python3.9/site-packages/botocore/retryhandler.py:359: in _check_caught_exception
    raise caught_exception
/usr/local/lib/python3.9/site-packages/botocore/endpoint.py:197: in _do_get_response
    responses = self._event_emitter.emit(event_name, request=request)
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:356: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:228: in emit
    return self._emit(event_name, kwargs)
/usr/local/lib/python3.9/site-packages/botocore/hooks.py:211: in _emit
    response = handler(**kwargs)
/usr/local/lib/python3.9/site-packages/moto/core/models.py:397: in __call__
    status, headers, body = response_callback(
/usr/local/lib/python3.9/site-packages/moto/core/responses.py:203: in dispatch
    return cls()._dispatch(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/moto/core/responses.py:313: in _dispatch
    return self.call_action()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <moto.glue.responses.GlueResponse object at 0x104e9e190>

    def call_action(self):
        headers = self.response_headers
    
        try:
            self._authenticate_and_authorize_normal_action()
        except HTTPException as http_error:
            response = http_error.description, dict(status=http_error.code)
            return self._send_response(headers, response)
    
        action = camelcase_to_underscores(self._get_action())
        method_names = method_names_from_class(self.__class__)
        if action in method_names:
            method = getattr(self, action)
            try:
                response = method()
            except HTTPException as http_error:
                response_headers = dict(http_error.get_headers() or [])
                response_headers["status"] = http_error.code
                response = http_error.description, response_headers
    
            if isinstance(response, str):
                return 200, headers, response
            else:
                return self._send_response(headers, response)
    
        if not action:
            return 404, headers, ""
    
>       raise NotImplementedError(
            "The {0} action has not been implemented".format(action)
        )
E       NotImplementedError: The delete_database action has not been implemented

/usr/local/lib/python3.9/site-packages/moto/core/responses.py:417: NotImplementedError
```


Discussion:
Hi @cgomez238, thanks for raising this! Marking it as an enhancement to add this method

Resolving patch:
diff --git a/moto/glue/models.py b/moto/glue/models.py
--- a/moto/glue/models.py
+++ b/moto/glue/models.py
@@ -58,6 +58,11 @@ def get_database(self, database_name):
     def get_databases(self):
         return [self.databases[key] for key in self.databases] if self.databases else []
 
+    def delete_database(self, database_name):
+        if database_name not in self.databases:
+            raise DatabaseNotFoundException(database_name)
+        del self.databases[database_name]
+
     def create_table(self, database_name, table_name, table_input):
         database = self.get_database(database_name)
 
diff --git a/moto/glue/responses.py b/moto/glue/responses.py
--- a/moto/glue/responses.py
+++ b/moto/glue/responses.py
@@ -35,6 +35,11 @@ def get_databases(self):
             {"DatabaseList": [database.as_dict() for database in database_list]}
         )
 
+    def delete_database(self):
+        name = self.parameters.get("Name")
+        self.glue_backend.delete_database(name)
+        return json.dumps({})
+
     def create_table(self):
         database_name = self.parameters.get("DatabaseName")
         table_input = self.parameters.get("TableInput")


Test patch:
diff --git a/tests/test_glue/test_datacatalog.py b/tests/test_glue/test_datacatalog.py
--- a/tests/test_glue/test_datacatalog.py
+++ b/tests/test_glue/test_datacatalog.py
@@ -90,6 +90,33 @@ def test_get_databases_several_items():
     database_list[1]["Name"].should.equal(database_name_2)
 
 
+@mock_glue
+def test_delete_database():
+    client = boto3.client("glue", region_name="us-east-1")
+    database_name_1, database_name_2 = "firstdatabase", "seconddatabase"
+
+    helpers.create_database(client, database_name_1, {"Name": database_name_1})
+    helpers.create_database(client, database_name_2, {"Name": database_name_2})
+
+    client.delete_database(Name=database_name_1)
+
+    database_list = sorted(
+        client.get_databases()["DatabaseList"], key=lambda x: x["Name"]
+    )
+    [db["Name"] for db in database_list].should.equal([database_name_2])
+
+
+@mock_glue
+def test_delete_unknown_database():
+    client = boto3.client("glue", region_name="us-east-1")
+
+    with pytest.raises(ClientError) as exc:
+        client.delete_database(Name="x")
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("EntityNotFoundException")
+    err["Message"].should.equal("Database x not found.")
+
+
 @mock_glue
 def test_create_table():
     client = boto3.client("glue", region_name="us-east-1")