summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py2
-rw-r--r--tests/features/steps/crud_database.py1
-rw-r--r--tests/test_application_name.py17
-rw-r--r--tests/test_main.py99
-rw-r--r--tests/test_pgexecute.py66
-rw-r--r--tests/test_ssh_tunnel.py4
6 files changed, 185 insertions, 4 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 33cddf2..e50f1fe 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -9,6 +9,7 @@ from utils import (
db_connection,
drop_tables,
)
+import pgcli.main
import pgcli.pgexecute
@@ -37,6 +38,7 @@ def executor(connection):
password=POSTGRES_PASSWORD,
port=POSTGRES_PORT,
dsn=None,
+ notify_callback=pgcli.main.notify_callback,
)
diff --git a/tests/features/steps/crud_database.py b/tests/features/steps/crud_database.py
index 87cdc85..9507d46 100644
--- a/tests/features/steps/crud_database.py
+++ b/tests/features/steps/crud_database.py
@@ -3,6 +3,7 @@ Steps for behavioral style tests are defined in this module.
Each step is defined by the string decorating it.
This string is used to call the step in "*.feature" file.
"""
+
import pexpect
from behave import when, then
diff --git a/tests/test_application_name.py b/tests/test_application_name.py
new file mode 100644
index 0000000..5fac5b2
--- /dev/null
+++ b/tests/test_application_name.py
@@ -0,0 +1,17 @@
+from unittest.mock import patch
+
+from click.testing import CliRunner
+
+from pgcli.main import cli
+from pgcli.pgexecute import PGExecute
+
+
+def test_application_name_in_env():
+ runner = CliRunner()
+ app_name = "wonderful_app"
+ with patch.object(PGExecute, "__init__") as mock_pgxecute:
+ runner.invoke(
+ cli, ["127.0.0.1:5432/hello", "user"], env={"PGAPPNAME": app_name}
+ )
+ kwargs = mock_pgxecute.call_args.kwargs
+ assert kwargs.get("application_name") == app_name
diff --git a/tests/test_main.py b/tests/test_main.py
index cbf20a6..3683d49 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,5 +1,8 @@
import os
import platform
+import re
+import tempfile
+import datetime
from unittest import mock
import pytest
@@ -11,7 +14,9 @@ except ImportError:
from pgcli.main import (
obfuscate_process_password,
+ duration_in_words,
format_output,
+ notify_callback,
PGCli,
OutputSettings,
COLOR_CODE_REGEX,
@@ -297,6 +302,24 @@ def test_i_works(tmpdir, executor):
@dbtest
+def test_toggle_verbose_errors(executor):
+ cli = PGCli(pgexecute=executor)
+
+ cli._evaluate_command("\\v on")
+ assert cli.verbose_errors
+ output, _ = cli._evaluate_command("SELECT 1/0")
+ assert "SQLSTATE" in output[0]
+
+ cli._evaluate_command("\\v off")
+ assert not cli.verbose_errors
+ output, _ = cli._evaluate_command("SELECT 1/0")
+ assert "SQLSTATE" not in output[0]
+
+ cli._evaluate_command("\\v")
+ assert cli.verbose_errors
+
+
+@dbtest
def test_echo_works(executor):
cli = PGCli(pgexecute=executor)
statement = r"\echo asdf"
@@ -313,6 +336,34 @@ def test_qecho_works(executor):
@dbtest
+def test_logfile_works(executor):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ log_file = f"{tmpdir}/tempfile.log"
+ cli = PGCli(pgexecute=executor, log_file=log_file)
+ statement = r"\qecho hello!"
+ cli.execute_command(statement)
+ with open(log_file, "r") as f:
+ log_contents = f.readlines()
+ assert datetime.datetime.fromisoformat(log_contents[0].strip())
+ assert log_contents[1].strip() == r"\qecho hello!"
+ assert log_contents[2].strip() == "hello!"
+
+
+@dbtest
+def test_logfile_unwriteable_file(executor):
+ cli = PGCli(pgexecute=executor)
+ statement = r"\log-file forbidden.log"
+ with mock.patch("builtins.open") as mock_open:
+ mock_open.side_effect = PermissionError(
+ "[Errno 13] Permission denied: 'forbidden.log'"
+ )
+ result = run(executor, statement, pgspecial=cli.pgspecial)
+ assert result == [
+ "[Errno 13] Permission denied: 'forbidden.log'\nLogfile capture disabled"
+ ]
+
+
+@dbtest
def test_watch_works(executor):
cli = PGCli(pgexecute=executor)
@@ -431,6 +482,7 @@ def test_pg_service_file(tmpdir):
"b_host",
"5435",
"",
+ notify_callback,
application_name="pgcli",
)
del os.environ["PGPASSWORD"]
@@ -486,5 +538,50 @@ def test_application_name_db_uri(tmpdir):
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
cli.connect_uri("postgres://bar@baz.com/?application_name=cow")
mock_pgexecute.assert_called_with(
- "bar", "bar", "", "baz.com", "", "", application_name="cow"
+ "bar", "bar", "", "baz.com", "", "", notify_callback, application_name="cow"
)
+
+
+@pytest.mark.parametrize(
+ "duration_in_seconds,words",
+ [
+ (0, "0 seconds"),
+ (0.0009, "0.001 second"),
+ (0.0005, "0.001 second"),
+ (0.0004, "0.0 second"), # not perfect, but will do
+ (0.2, "0.2 second"),
+ (1, "1 second"),
+ (1.4, "1 second"),
+ (2, "2 seconds"),
+ (3.4, "3 seconds"),
+ (60, "1 minute"),
+ (61, "1 minute 1 second"),
+ (123, "2 minutes 3 seconds"),
+ (3600, "1 hour"),
+ (7235, "2 hours 35 seconds"),
+ (9005, "2 hours 30 minutes 5 seconds"),
+ (86401, "24 hours 1 second"),
+ ],
+)
+def test_duration_in_words(duration_in_seconds, words):
+ assert duration_in_words(duration_in_seconds) == words
+
+
+@dbtest
+def test_notifications(executor):
+ run(executor, "listen chan1")
+
+ with mock.patch("pgcli.main.click.secho") as mock_secho:
+ run(executor, "notify chan1, 'testing1'")
+ mock_secho.assert_called()
+ arg = mock_secho.call_args_list[0].args[0]
+ assert re.match(
+ r'Notification received on channel "chan1" \(PID \d+\):\ntesting1',
+ arg,
+ )
+
+ run(executor, "unlisten chan1")
+
+ with mock.patch("pgcli.main.click.secho") as mock_secho:
+ run(executor, "notify chan1, 'testing2'")
+ mock_secho.assert_not_called()
diff --git a/tests/test_pgexecute.py b/tests/test_pgexecute.py
index 636795b..f1cadfd 100644
--- a/tests/test_pgexecute.py
+++ b/tests/test_pgexecute.py
@@ -1,3 +1,4 @@
+import re
from textwrap import dedent
import psycopg
@@ -6,7 +7,7 @@ from unittest.mock import patch, MagicMock
from pgspecial.main import PGSpecial, NO_QUERY
from utils import run, dbtest, requires_json, requires_jsonb
-from pgcli.main import PGCli
+from pgcli.main import PGCli, exception_formatter as main_exception_formatter
from pgcli.packages.parseutils.meta import FunctionMetadata
@@ -219,8 +220,33 @@ def test_database_list(executor):
@dbtest
def test_invalid_syntax(executor, exception_formatter):
- result = run(executor, "invalid syntax!", exception_formatter=exception_formatter)
+ result = run(
+ executor,
+ "invalid syntax!",
+ exception_formatter=lambda x: main_exception_formatter(x, verbose_errors=False),
+ )
assert 'syntax error at or near "invalid"' in result[0]
+ assert "SQLSTATE" not in result[0]
+
+
+@dbtest
+def test_invalid_syntax_verbose(executor):
+ result = run(
+ executor,
+ "invalid syntax!",
+ exception_formatter=lambda x: main_exception_formatter(x, verbose_errors=True),
+ )
+ fields = r"""
+Severity: ERROR
+Severity \(non-localized\): ERROR
+SQLSTATE code: 42601
+Message: syntax error at or near "invalid"
+Position: 1
+File: scan\.l
+Line: \d+
+Routine: scanner_yyerror
+ """.strip()
+ assert re.search(fields, result[0])
@dbtest
@@ -691,6 +717,38 @@ def test_function_definition(executor):
@dbtest
+def test_function_notice_order(executor):
+ run(
+ executor,
+ """
+ CREATE OR REPLACE FUNCTION demo_order() RETURNS VOID AS
+ $$
+ BEGIN
+ RAISE NOTICE 'first';
+ RAISE NOTICE 'second';
+ RAISE NOTICE 'third';
+ RAISE NOTICE 'fourth';
+ RAISE NOTICE 'fifth';
+ RAISE NOTICE 'sixth';
+ END;
+ $$
+ LANGUAGE plpgsql;
+ """,
+ )
+
+ executor.function_definition("demo_order")
+
+ result = run(executor, "select demo_order()")
+ assert "first\nsecond\nthird\nfourth\nfifth\nsixth" in result[0]
+ assert "+------------+" in result[1]
+ assert "| demo_order |" in result[2]
+ assert "|------------|" in result[3]
+ assert "| |" in result[4]
+ assert "+------------+" in result[5]
+ assert "SELECT 1" in result[6]
+
+
+@dbtest
def test_view_definition(executor):
run(executor, "create table tbl1 (a text, b numeric)")
run(executor, "create view vw1 AS SELECT * FROM tbl1")
@@ -721,6 +779,10 @@ def test_short_host(executor):
executor, "host", "localhost1.example.org,localhost2.example.org"
):
assert executor.short_host == "localhost1"
+ with patch.object(executor, "host", "ec2-11-222-333-444.compute-1.amazonaws.com"):
+ assert executor.short_host == "ec2-11-222-333-444"
+ with patch.object(executor, "host", "1.2.3.4"):
+ assert executor.short_host == "1.2.3.4"
class VirtualCursor:
diff --git a/tests/test_ssh_tunnel.py b/tests/test_ssh_tunnel.py
index ae865f4..983212b 100644
--- a/tests/test_ssh_tunnel.py
+++ b/tests/test_ssh_tunnel.py
@@ -6,7 +6,7 @@ from configobj import ConfigObj
from click.testing import CliRunner
from sshtunnel import SSHTunnelForwarder
-from pgcli.main import cli, PGCli
+from pgcli.main import cli, notify_callback, PGCli
from pgcli.pgexecute import PGExecute
@@ -61,6 +61,7 @@ def test_ssh_tunnel(
"127.0.0.1",
pgcli.ssh_tunnel.local_bind_ports[0],
"",
+ notify_callback,
)
mock_ssh_tunnel_forwarder.reset_mock()
mock_pgexecute.reset_mock()
@@ -96,6 +97,7 @@ def test_ssh_tunnel(
"127.0.0.1",
pgcli.ssh_tunnel.local_bind_ports[0],
"",
+ notify_callback,
)
mock_ssh_tunnel_forwarder.reset_mock()
mock_pgexecute.reset_mock()