Repository: litestar-org/litestar

Issue:
Bug: SQLAlchemy 1 plugin mutates app state destructively
### Description

When using the SQLAlchemy 1 plugin, repeatedly running through the application lifecycle (as done when testing an application not provided by a factory function), causes a `KeyError` on the second pass.

This is caused be the plugin's `on_shutdown` handler deleting the `engine_app_state_key` from the application's state on application shutdown, but only *adding* it on application init.

This can be solved by setting up the application state within `on_startup`.

### URL to code causing the issue

_No response_

### MCVE

```python
from starlite.contrib.sqlalchemy_1.config import SQLAlchemyConfig
from starlite.contrib.sqlalchemy_1.plugin import SQLAlchemyPlugin
from starlite import Starlite
from starlite.testing import TestClient

app = Starlite(
    plugins=[
        SQLAlchemyPlugin(
            config=SQLAlchemyConfig(
                connection_string="sqlite+pysqlite:///:memory:",
                use_async_engine=False,
            )
        )
    ]
)


with TestClient(app) as client:
    pass


with TestClient(app) as client:
    pass
```


### Steps to reproduce

_No response_

### Screenshots

```bash
"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"
```


### Logs

_No response_

### Starlite Version

This affects both 1.51 and the main branch.

### Platform

- [ ] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)


Discussion:
Can confirm.

We have this patch in `starlite-pg-redis-docker`:

```py
@pytest.fixture(scope="session", autouse=True)
def _patch_sqlalchemy_plugin() -> "abc.Iterator":
    monkeypatch = pytest.MonkeyPatch()
    monkeypatch.setattr(
        sqlalchemy_plugin.SQLAlchemyConfig,  # type:ignore[attr-defined]
        "on_shutdown",
        MagicMock(),
    )
    yield
    monkeypatch.undo()
```
Adding an `on_startup` handler feels like the correct way to handle. Good idea!

Resolving patch:
diff --git a/starlite/contrib/sqlalchemy_1/plugin.py b/starlite/contrib/sqlalchemy_1/plugin.py
--- a/starlite/contrib/sqlalchemy_1/plugin.py
+++ b/starlite/contrib/sqlalchemy_1/plugin.py
@@ -85,9 +85,9 @@ def on_app_init(self, app: "Starlite") -> None:
         if self._config is not None:
             app.dependencies[self._config.dependency_key] = Provide(self._config.create_db_session_dependency)
             app.before_send.append(self._config.before_send_handler)  # type: ignore[arg-type]
+            app.on_startup.append(self._config.update_app_state)
             app.on_shutdown.append(self._config.on_shutdown)
             self._config.config_sql_alchemy_logging(app.logging_config)
-            self._config.update_app_state(state=app.state)
 
     @staticmethod
     def is_plugin_supported_type(value: Any) -> "TypeGuard[DeclarativeMeta]":


Test patch:
diff --git a/tests/contrib/sqlalchemy_1/sql_alchemy_plugin/test_sql_alchemy_config.py b/tests/contrib/sqlalchemy_1/sql_alchemy_plugin/test_sql_alchemy_config.py
--- a/tests/contrib/sqlalchemy_1/sql_alchemy_plugin/test_sql_alchemy_config.py
+++ b/tests/contrib/sqlalchemy_1/sql_alchemy_plugin/test_sql_alchemy_config.py
@@ -24,17 +24,17 @@
 @pytest.mark.parametrize("connection_string", ["sqlite+aiosqlite://", "sqlite://"])
 def test_sets_engine_and_session_maker(connection_string: str) -> None:
     config = SQLAlchemyConfig(connection_string=connection_string, use_async_engine="+aiosqlite" in connection_string)
-    app = Starlite(plugins=[SQLAlchemyPlugin(config=config)])
-    assert app.state.get(config.engine_app_state_key)
-    assert app.state.get(config.session_maker_app_state_key)
+    with create_test_client([], plugins=[SQLAlchemyPlugin(config=config)]) as client:
+        assert client.app.state.get(config.engine_app_state_key)
+        assert client.app.state.get(config.session_maker_app_state_key)
 
 
 @pytest.mark.parametrize("connection_string", ["sqlite+aiosqlite://", "sqlite://"])
 def test_dependency_creates_session(connection_string: str) -> None:
     config = SQLAlchemyConfig(connection_string=connection_string, use_async_engine="+aiosqlite" in connection_string)
-    app = Starlite(plugins=[SQLAlchemyPlugin(config=config)])
-    request = RequestFactory().get()
-    session = config.create_db_session_dependency(state=app.state, scope=request.scope)
+    with create_test_client([], plugins=[SQLAlchemyPlugin(config=config)]) as client:
+        request = RequestFactory().get()
+        session = config.create_db_session_dependency(state=client.app.state, scope=request.scope)
     assert session
     assert request.scope[SESSION_SCOPE_KEY]  # type: ignore