Build reusable bot framework
This commit is contained in:
78
tests/unit/test_postgres.py
Normal file
78
tests/unit/test_postgres.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from core import postgres
|
||||
|
||||
|
||||
def test_safe_identifier_quotes_names_and_rejects_sql_expressions():
|
||||
assert postgres._safe_id("scheduled_jobs") == '"scheduled_jobs"'
|
||||
|
||||
for unsafe in ["jobs.id", "jobs; DROP TABLE jobs", "two words", "", 7, None]:
|
||||
with pytest.raises(ValueError, match="Invalid SQL identifier"):
|
||||
postgres._safe_id(unsafe)
|
||||
|
||||
|
||||
def test_order_clause_allows_only_identifiers_and_directions():
|
||||
assert postgres._order_clause(
|
||||
["created_at desc", ("id", "ASC")]
|
||||
) == '"created_at" DESC, "id" ASC'
|
||||
|
||||
unsafe_values = [
|
||||
"created_at DESC NULLS LAST",
|
||||
"created_at;drop DESC",
|
||||
[("created_at", "SIDEWAYS")],
|
||||
[("created_at", "ASC", "extra")],
|
||||
]
|
||||
for value in unsafe_values:
|
||||
with pytest.raises(ValueError):
|
||||
postgres._order_clause(value)
|
||||
|
||||
|
||||
def test_select_builds_parameterized_where_and_safe_order(monkeypatch):
|
||||
cursor = MagicMock()
|
||||
cursor.fetchall.return_value = [{"id": "job-1"}]
|
||||
|
||||
@contextmanager
|
||||
def fake_cursor():
|
||||
yield cursor
|
||||
|
||||
monkeypatch.setattr(postgres, "get_cursor", fake_cursor)
|
||||
|
||||
rows = postgres.select(
|
||||
"scheduled_jobs",
|
||||
where={"user_uuid": "user-1", "status": ("IN", ["pending", "running"])},
|
||||
order_by=[("run_at", "ASC"), ("id", "DESC")],
|
||||
limit=10,
|
||||
)
|
||||
|
||||
assert rows == [{"id": "job-1"}]
|
||||
query, params = cursor.execute.call_args.args
|
||||
assert 'FROM "scheduled_jobs"' in query
|
||||
assert '"user_uuid" = %(user_uuid_0)s' in query
|
||||
assert '"status" IN (%(status_1_0)s, %(status_1_1)s)' in query
|
||||
assert 'ORDER BY "run_at" ASC, "id" DESC' in query
|
||||
assert "LIMIT %(query_limit)s" in query
|
||||
assert params == {
|
||||
"user_uuid_0": "user-1",
|
||||
"status_1_0": "pending",
|
||||
"status_1_1": "running",
|
||||
"query_limit": 10,
|
||||
}
|
||||
|
||||
|
||||
def test_empty_update_and_delete_conditions_fail_before_opening_cursor(monkeypatch):
|
||||
cursor_factory = MagicMock(
|
||||
side_effect=AssertionError("a database cursor must not be opened")
|
||||
)
|
||||
monkeypatch.setattr(postgres, "get_cursor", cursor_factory)
|
||||
|
||||
with pytest.raises(ValueError, match="update data cannot be empty"):
|
||||
postgres.update("users", {}, {"id": "user-1"})
|
||||
with pytest.raises(ValueError, match="non-empty where"):
|
||||
postgres.update("users", {"timezone": "UTC"}, {})
|
||||
with pytest.raises(ValueError, match="non-empty where"):
|
||||
postgres.delete("users", {})
|
||||
|
||||
cursor_factory.assert_not_called()
|
||||
Reference in New Issue
Block a user