EEEEEEEEEEE.....                                                         [100%]
=================================== ERRORS ====================================
_____________________ ERROR at setup of test_health_live ______________________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEB75EB10>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6990>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB67BD10>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEB776D50>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6990>
cursor = <sqlite3.Cursor object at 0x0000019AEB7F77C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB67BD10>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB6EB620>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEB75DCD0>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6990>
cursor = <sqlite3.Cursor object at 0x0000019AEB7F77C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB67BD10>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
____________ ERROR at setup of test_login_sets_cookie_and_session _____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBAB5E50>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDC6710>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896450>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBAC9050>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDC6710>
cursor = <sqlite3.Cursor object at 0x0000019AEE5A98C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896450>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D52B0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEB75F490>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDC6710>
cursor = <sqlite3.Cursor object at 0x0000019AEE5A98C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896450>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
_____________ ERROR at setup of test_login_wrong_password_audited _____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEB7CE3F0>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B7250>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896210>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBACA550>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B7250>
cursor = <sqlite3.Cursor object at 0x0000019AEBC6F740>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896210>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D7E00>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEB73E450>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B7250>
cursor = <sqlite3.Cursor object at 0x0000019AEBC6F740>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896210>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
_____________ ERROR at setup of test_unauthenticated_api_rejected _____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBACDB80>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6210>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB895C10>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBAC9C50>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6210>
cursor = <sqlite3.Cursor object at 0x0000019AED6F5840>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB895C10>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D5940>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBA88160>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6210>
cursor = <sqlite3.Cursor object at 0x0000019AED6F5840>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB895C10>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
____________ ERROR at setup of test_auditor_cannot_create_clients _____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBD814F0>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6FD0>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896F90>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBDDD550>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6FD0>
cursor = <sqlite3.Cursor object at 0x0000019AEB7F7840>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896F90>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D5E80>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBA8A8B0>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEB7B6FD0>
cursor = <sqlite3.Cursor object at 0x0000019AEB7F7840>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896F90>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
_________ ERROR at setup of test_create_client_returns_password_once __________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBDD64E0>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA20D0>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897290>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBDDDF50>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA20D0>
cursor = <sqlite3.Cursor object at 0x0000019AEC086F40>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897290>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D5D30>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBDDDE50>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA20D0>
cursor = <sqlite3.Cursor object at 0x0000019AEC086F40>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897290>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
_____________ ERROR at setup of test_disable_enable_delete_client _____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBFE65D0>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA2E90>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897DD0>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBCE4850>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA2E90>
cursor = <sqlite3.Cursor object at 0x0000019AEBDB50C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897DD0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB7D5BE0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBDDFE50>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA2E90>
cursor = <sqlite3.Cursor object at 0x0000019AEBDB50C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB897DD0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
__________________ ERROR at setup of test_role_acl_lifecycle __________________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBDCA300>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3890>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8968D0>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBCE5F50>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3890>
cursor = <sqlite3.Cursor object at 0x0000019AEBAFA8C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8968D0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEB6EB380>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBDC8F50>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3890>
cursor = <sqlite3.Cursor object at 0x0000019AEBAFA8C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8968D0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
___________________ ERROR at setup of test_group_lifecycle ____________________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBB4A120>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3C50>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896D50>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBBA0450>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3C50>
cursor = <sqlite3.Cursor object at 0x0000019AEE5AABC0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896D50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEDC8D2B0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBB48C80>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3C50>
cursor = <sqlite3.Cursor object at 0x0000019AEE5AABC0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB896D50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
___________ ERROR at setup of test_listener_management_and_preview ____________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBB42030>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3D90>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEE96C650>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBBA2350>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3D90>
cursor = <sqlite3.Cursor object at 0x0000019AED6F4DC0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEE96C650>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEDC8D400>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBDCE6D0>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBDA3D90>
cursor = <sqlite3.Cursor object at 0x0000019AED6F4DC0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEE96C650>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
________________ ERROR at setup of test_audit_events_recorded _________________

self = <sqlalchemy.engine.base.Connection object at 0x0000019AEBB5EB70>
dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBB80410>
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8971D0>
statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler object at 0x0000019AEBBA2F50>
parameters = [('SUPER_ADMIN',)]

    def _exec_single_context(
        self,
        dialect: Dialect,
        context: ExecutionContext,
        statement: Union[str, Compiled],
        parameters: Optional[_AnyMultiExecuteParams],
    ) -> CursorResult[Any]:
        """continue the _execute_context() method for a single DBAPI
        cursor.execute() or cursor.executemany() call.
    
        """
        if dialect.bind_typing is BindTyping.SETINPUTSIZES:
            generic_setinputsizes = context._prepare_set_input_sizes()
    
            if generic_setinputsizes:
                try:
                    dialect.do_set_input_sizes(
                        context.cursor, generic_setinputsizes, context
                    )
                except BaseException as e:
                    self._handle_dbapi_exception(
                        e, str(statement), parameters, None, context
                    )
    
        cursor, str_statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        effective_parameters: Optional[_AnyExecuteParams]
    
        if not context.executemany:
            effective_parameters = parameters[0]
        else:
            effective_parameters = parameters
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                str_statement, effective_parameters = fn(
                    self,
                    cursor,
                    str_statement,
                    effective_parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self._log_info(str_statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        effective_parameters,
                        batches=10,
                        ismulti=context.executemany,
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]",
                    stats,
                )
    
        evt_handled: bool = False
        try:
            if context.execute_style is ExecuteStyle.EXECUTEMANY:
                effective_parameters = cast(
                    "_CoreMultiExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor,
                        str_statement,
                        effective_parameters,
                        context,
                    )
            elif not effective_parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, str_statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, str_statement, context
                    )
            else:
                effective_parameters = cast(
                    "_CoreSingleExecuteParams", effective_parameters
                )
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(
                            cursor,
                            str_statement,
                            effective_parameters,
                            context,
                        ):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, str_statement, effective_parameters, context
                    )

.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBB80410>
cursor = <sqlite3.Cursor object at 0x0000019AEE18D6C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8971D0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlite3.OperationalError: no such table: users

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError

The above exception was the direct cause of the following exception:

db_session = <sqlalchemy.orm.session.Session object at 0x0000019AEDC8DA90>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000019AEBBC2DD0>

    @pytest.fixture()
    def client(db_session, monkeypatch):
        """TestClient wired to the fixture DB; dynsec calls are faked out."""
        # Point the app's session factory at the test engine.
        monkeypatch.setattr(db_module, "_SessionFactory", None)
        monkeypatch.setattr(
            db_module, "_engine", db_session.get_bind()
        )
    
        # Fake dynsec so no broker is needed for API tests.
        class FakeDynsec:
            def __init__(self):
                self.clients: dict[str, dict] = {}
                self.roles: dict[str, dict] = {}
                self.groups: dict[str, dict] = {}
    
            def _command(self, command, **kwargs):
                return {"success": True}
    
            def create_client(self, username, password, **kwargs):
                self.clients[username] = {"username": username, "enabled": True, **kwargs}
    
            def list_clients(self):
                return list(self.clients.values())
    
            def delete_client(self, username):
                self.clients.pop(username, None)
    
            def disable_client(self, username):
                self.clients[username]["enabled"] = False
    
            def enable_client(self, username):
                self.clients[username]["enabled"] = True
    
            def set_client_password(self, username, password):
                pass
    
            def get_client(self, username):
                return self.clients[username]
    
            def create_group(self, groupname, roles=None):
                self.groups[groupname] = {"groupname": groupname, "roles": roles or []}
    
            def list_groups(self):
                return list(self.groups.values())
    
            def delete_group(self, groupname):
                self.groups.pop(groupname, None)
    
            def create_role(self, rolename, acls=None):
                self.roles[rolename] = {"rolename": rolename, "acls": acls or []}
    
            def list_roles(self):
                return list(self.roles.values())
    
            def delete_role(self, rolename):
                self.roles.pop(rolename, None)
    
            def set_role_acls(self, rolename, acls):
                self.roles[rolename]["acls"] = acls
    
            def get_role(self, rolename):
                return self.roles[rolename]
    
            def connect(self):
                pass
    
            def close(self):
                pass
    
        fake = FakeDynsec()
        monkeypatch.setattr(main_module, "dynsec_client", fake)
    
        from app.routers import mqtt as mqtt_router
    
        monkeypatch.setattr(mqtt_router, "dynsec_client", fake)
    
>       with TestClient(main_module.app) as test_client:
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests\conftest.py:132: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv\Lib\site-packages\starlette\testclient.py:698: in __enter__
    portal.call(self.wait_startup)
.venv\Lib\site-packages\anyio\from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:733: in wait_startup
    await receive()
.venv\Lib\site-packages\starlette\testclient.py:724: in receive
    self.task.result()
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\concurrent\futures\_base.py:395: in __get_result
    raise self._exception
.venv\Lib\site-packages\anyio\from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\starlette\testclient.py:714: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
.venv\Lib\site-packages\fastapi\applications.py:1163: in __call__
    await super().__call__(scope, receive, send)
.venv\Lib\site-packages\starlette\applications.py:96: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\errors.py:151: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\base.py:103: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\cors.py:80: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\middleware\exceptions.py:49: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:670: in __call__
    await self.middleware_stack(scope, receive, send)
.venv\Lib\site-packages\fastapi\routing.py:2726: in app
    await self.lifespan(scope, receive, send)
.venv\Lib\site-packages\starlette\routing.py:648: in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
.venv\Lib\site-packages\fastapi\routing.py:240: in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
..\..\..\..\AppData\Local\Python\pythoncore-3.14-64\Lib\contextlib.py:214: in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
app\main.py:65: in lifespan
    _bootstrap_admin()
app\main.py:34: in _bootstrap_admin
    has_admin = session.scalar(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2421: in scalar
    return self._execute_internal(
.venv\Lib\site-packages\sqlalchemy\orm\session.py:2271: in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
.venv\Lib\site-packages\sqlalchemy\orm\context.py:306: in orm_execute_statement
    result = conn.execute(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1421: in execute
    return meth(
.venv\Lib\site-packages\sqlalchemy\sql\elements.py:526: in _execute_on_connection
    return connection._execute_clauseelement(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1643: in _execute_clauseelement
    ret = self._execute_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1848: in _execute_context
    return self._exec_single_context(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1988: in _exec_single_context
    self._handle_dbapi_exception(
.venv\Lib\site-packages\sqlalchemy\engine\base.py:2365: in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv\Lib\site-packages\sqlalchemy\engine\base.py:1969: in _exec_single_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite object at 0x0000019AEBB80410>
cursor = <sqlite3.Cursor object at 0x0000019AEE18D6C0>
statement = 'SELECT users.id \nFROM users \nWHERE users.role = ?'
parameters = ('SUPER_ADMIN',)
context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext object at 0x0000019AEB8971D0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
E       [SQL: SELECT users.id 
E       FROM users 
E       WHERE users.role = ?]
E       [parameters: ('SUPER_ADMIN',)]
E       (Background on this error at: https://sqlalche.me/e/20/e3q8)

.venv\Lib\site-packages\sqlalchemy\engine\default.py:952: OperationalError
============================== warnings summary ===============================
.venv\Lib\site-packages\fastapi\testclient.py:1
  C:\Users\jonas\Documents\Git\mqtt-broker-stack\backend\.venv\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
    from starlette.testclient import TestClient as TestClient  # noqa

.venv\Lib\site-packages\starlette\testclient.py:53
  C:\Users\jonas\Documents\Git\mqtt-broker-stack\backend\.venv\Lib\site-packages\starlette\testclient.py:53: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ===========================
ERROR tests/test_api.py::test_health_live - sqlalchemy.exc.OperationalError: ...
ERROR tests/test_api.py::test_login_sets_cookie_and_session - sqlalchemy.exc....
ERROR tests/test_api.py::test_login_wrong_password_audited - sqlalchemy.exc.O...
ERROR tests/test_api.py::test_unauthenticated_api_rejected - sqlalchemy.exc.O...
ERROR tests/test_api.py::test_auditor_cannot_create_clients - sqlalchemy.exc....
ERROR tests/test_api.py::test_create_client_returns_password_once - sqlalchem...
ERROR tests/test_api.py::test_disable_enable_delete_client - sqlalchemy.exc.O...
ERROR tests/test_api.py::test_role_acl_lifecycle - sqlalchemy.exc.Operational...
ERROR tests/test_api.py::test_group_lifecycle - sqlalchemy.exc.OperationalErr...
ERROR tests/test_api.py::test_listener_management_and_preview - sqlalchemy.ex...
ERROR tests/test_api.py::test_audit_events_recorded - sqlalchemy.exc.Operatio...
5 passed, 2 warnings, 11 errors in 11.71s

