summaryrefslogtreecommitdiffstats
path: root/tests/features
diff options
context:
space:
mode:
Diffstat (limited to 'tests/features')
-rw-r--r--tests/features/basic_commands.feature24
-rw-r--r--tests/features/crud_database.feature2
-rw-r--r--tests/features/crud_table.feature29
-rw-r--r--tests/features/environment.py14
-rw-r--r--tests/features/expanded.feature6
-rw-r--r--tests/features/steps/basic_commands.py97
-rw-r--r--tests/features/steps/crud_table.py83
-rw-r--r--tests/features/steps/expanded.py2
-rw-r--r--tests/features/steps/wrappers.py5
9 files changed, 235 insertions, 27 deletions
diff --git a/tests/features/basic_commands.feature b/tests/features/basic_commands.feature
index cd15306..ee497b9 100644
--- a/tests/features/basic_commands.feature
+++ b/tests/features/basic_commands.feature
@@ -23,6 +23,30 @@ Feature: run the cli,
When we send "ctrl + d"
then dbcli exits
+ Scenario: confirm exit when a transaction is ongoing
+ When we begin transaction
+ and we try to send "ctrl + d"
+ then we see ongoing transaction message
+ when we send "c"
+ then dbcli exits
+
+ Scenario: cancel exit when a transaction is ongoing
+ When we begin transaction
+ and we try to send "ctrl + d"
+ then we see ongoing transaction message
+ when we send "a"
+ then we see dbcli prompt
+ when we rollback transaction
+ when we send "ctrl + d"
+ then dbcli exits
+
+ Scenario: interrupt current query via "ctrl + c"
+ When we send sleep query
+ and we send "ctrl + c"
+ then we see cancelled query warning
+ when we check for any non-idle sleep queries
+ then we don't see any non-idle sleep queries
+
Scenario: list databases
When we list databases
then we see list of databases
diff --git a/tests/features/crud_database.feature b/tests/features/crud_database.feature
index ed13bbe..87da4e3 100644
--- a/tests/features/crud_database.feature
+++ b/tests/features/crud_database.feature
@@ -5,7 +5,7 @@ Feature: manipulate databases:
When we create database
then we see database created
when we drop database
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see database dropped
when we connect to dbserver
then we see database connected
diff --git a/tests/features/crud_table.feature b/tests/features/crud_table.feature
index 1f9db4a..8a43c5c 100644
--- a/tests/features/crud_table.feature
+++ b/tests/features/crud_table.feature
@@ -8,15 +8,38 @@ Feature: manipulate tables:
then we see table created
when we insert into table
then we see record inserted
+ when we select from table
+ then we see data selected: initial
when we update table
then we see record updated
when we select from table
- then we see data selected
+ then we see data selected: updated
when we delete from table
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see record deleted
when we drop table
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see table dropped
when we connect to dbserver
then we see database connected
+
+ Scenario: transaction handling, with cancelling on a destructive warning.
+ When we connect to test database
+ then we see database connected
+ when we create table
+ then we see table created
+ when we begin transaction
+ then we see transaction began
+ when we insert into table
+ then we see record inserted
+ when we delete from table
+ then we respond to the destructive warning: n
+ when we select from table
+ then we see data selected: initial
+ when we rollback transaction
+ then we see transaction rolled back
+ when we select from table
+ then we see select output without data
+ when we drop table
+ then we respond to the destructive warning: y
+ then we see table dropped
diff --git a/tests/features/environment.py b/tests/features/environment.py
index 6cc8e14..50ac5fa 100644
--- a/tests/features/environment.py
+++ b/tests/features/environment.py
@@ -164,10 +164,24 @@ def before_step(context, _):
context.atprompt = False
+def is_known_problem(scenario):
+ """TODO: why is this not working in 3.12?"""
+ if sys.version_info >= (3, 12):
+ return scenario.name in (
+ 'interrupt current query via "ctrl + c"',
+ "run the cli with --username",
+ "run the cli with --user",
+ "run the cli with --port",
+ )
+ return False
+
+
def before_scenario(context, scenario):
if scenario.name == "list databases":
# not using the cli for that
return
+ if is_known_problem(scenario):
+ scenario.skip()
currentdb = None
if "pgbouncer" in scenario.feature.tags:
if context.pgbouncer_available:
diff --git a/tests/features/expanded.feature b/tests/features/expanded.feature
index 4f381f8..e486048 100644
--- a/tests/features/expanded.feature
+++ b/tests/features/expanded.feature
@@ -7,7 +7,7 @@ Feature: expanded mode:
and we select from table
then we see expanded data selected
when we drop table
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see table dropped
Scenario: expanded off
@@ -16,7 +16,7 @@ Feature: expanded mode:
and we select from table
then we see nonexpanded data selected
when we drop table
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see table dropped
Scenario: expanded auto
@@ -25,5 +25,5 @@ Feature: expanded mode:
and we select from table
then we see auto data selected
when we drop table
- then we confirm the destructive warning
+ then we respond to the destructive warning: y
then we see table dropped
diff --git a/tests/features/steps/basic_commands.py b/tests/features/steps/basic_commands.py
index 7c87814..687bdc0 100644
--- a/tests/features/steps/basic_commands.py
+++ b/tests/features/steps/basic_commands.py
@@ -64,13 +64,83 @@ def step_ctrl_d(context):
"""
Send Ctrl + D to hopefully exit.
"""
+ step_try_to_ctrl_d(context)
+ context.cli.expect(pexpect.EOF, timeout=5)
+ context.exit_sent = True
+
+
+@when('we try to send "ctrl + d"')
+def step_try_to_ctrl_d(context):
+ """
+ Send Ctrl + D, perhaps exiting, perhaps not (if a transaction is
+ ongoing).
+ """
# turn off pager before exiting
context.cli.sendcontrol("c")
context.cli.sendline(r"\pset pager off")
wrappers.wait_prompt(context)
context.cli.sendcontrol("d")
- context.cli.expect(pexpect.EOF, timeout=5)
- context.exit_sent = True
+
+
+@when('we send "ctrl + c"')
+def step_ctrl_c(context):
+ """Send Ctrl + c to hopefully interrupt."""
+ context.cli.sendcontrol("c")
+
+
+@then("we see cancelled query warning")
+def step_see_cancelled_query_warning(context):
+ """
+ Make sure we receive the warning that the current query was cancelled.
+ """
+ wrappers.expect_exact(context, "cancelled query", timeout=2)
+
+
+@then("we see ongoing transaction message")
+def step_see_ongoing_transaction_error(context):
+ """
+ Make sure we receive the warning that a transaction is ongoing.
+ """
+ context.cli.expect("A transaction is ongoing.", timeout=2)
+
+
+@when("we send sleep query")
+def step_send_sleep_15_seconds(context):
+ """
+ Send query to sleep for 15 seconds.
+ """
+ context.cli.sendline("select pg_sleep(15)")
+
+
+@when("we check for any non-idle sleep queries")
+def step_check_for_active_sleep_queries(context):
+ """
+ Send query to check for any non-idle pg_sleep queries.
+ """
+ context.cli.sendline(
+ "select state from pg_stat_activity where query not like '%pg_stat_activity%' and query like '%pg_sleep%' and state != 'idle';"
+ )
+
+
+@then("we don't see any non-idle sleep queries")
+def step_no_active_sleep_queries(context):
+ """Confirm that any pg_sleep queries are either idle or not active."""
+ wrappers.expect_exact(
+ context,
+ context.conf["pager_boundary"]
+ + "\r"
+ + dedent(
+ """
+ +-------+\r
+ | state |\r
+ |-------|\r
+ +-------+\r
+ SELECT 0\r
+ """
+ )
+ + context.conf["pager_boundary"],
+ timeout=5,
+ )
@when(r'we send "\?" command')
@@ -131,18 +201,31 @@ def step_see_found(context):
)
-@then("we confirm the destructive warning")
-def step_confirm_destructive_command(context):
- """Confirm destructive command."""
+@then("we respond to the destructive warning: {response}")
+def step_resppond_to_destructive_command(context, response):
+ """Respond to destructive command."""
wrappers.expect_exact(
context,
- "You're about to run a destructive command.\r\nDo you want to proceed? (y/n):",
+ "You're about to run a destructive command.\r\nDo you want to proceed? [y/N]:",
timeout=2,
)
- context.cli.sendline("y")
+ context.cli.sendline(response.strip())
@then("we send password")
def step_send_password(context):
wrappers.expect_exact(context, "Password for", timeout=5)
context.cli.sendline(context.conf["pass"] or "DOES NOT MATTER")
+
+
+@when('we send "{text}"')
+def step_send_text(context, text):
+ context.cli.sendline(text)
+ # Try to detect whether we are exiting. If so, set `exit_sent`
+ # so that `after_scenario` correctly cleans up.
+ try:
+ context.cli.expect(pexpect.EOF, timeout=0.2)
+ except pexpect.TIMEOUT:
+ pass
+ else:
+ context.exit_sent = True
diff --git a/tests/features/steps/crud_table.py b/tests/features/steps/crud_table.py
index 0375883..27d543e 100644
--- a/tests/features/steps/crud_table.py
+++ b/tests/features/steps/crud_table.py
@@ -9,6 +9,10 @@ from textwrap import dedent
import wrappers
+INITIAL_DATA = "xxx"
+UPDATED_DATA = "yyy"
+
+
@when("we create table")
def step_create_table(context):
"""
@@ -22,7 +26,7 @@ def step_insert_into_table(context):
"""
Send insert into table.
"""
- context.cli.sendline("""insert into a(x) values('xxx');""")
+ context.cli.sendline(f"""insert into a(x) values('{INITIAL_DATA}');""")
@when("we update table")
@@ -30,7 +34,9 @@ def step_update_table(context):
"""
Send insert into table.
"""
- context.cli.sendline("""update a set x = 'yyy' where x = 'xxx';""")
+ context.cli.sendline(
+ f"""update a set x = '{UPDATED_DATA}' where x = '{INITIAL_DATA}';"""
+ )
@when("we select from table")
@@ -46,7 +52,7 @@ def step_delete_from_table(context):
"""
Send deete from table.
"""
- context.cli.sendline("""delete from a where x = 'yyy';""")
+ context.cli.sendline(f"""delete from a where x = '{UPDATED_DATA}';""")
@when("we drop table")
@@ -57,6 +63,30 @@ def step_drop_table(context):
context.cli.sendline("drop table a;")
+@when("we alter the table")
+def step_alter_table(context):
+ """
+ Alter the table by adding a column.
+ """
+ context.cli.sendline("""alter table a add column y varchar;""")
+
+
+@when("we begin transaction")
+def step_begin_transaction(context):
+ """
+ Begin transaction
+ """
+ context.cli.sendline("begin;")
+
+
+@when("we rollback transaction")
+def step_rollback_transaction(context):
+ """
+ Rollback transaction
+ """
+ context.cli.sendline("rollback;")
+
+
@then("we see table created")
def step_see_table_created(context):
"""
@@ -81,19 +111,20 @@ def step_see_record_updated(context):
wrappers.expect_pager(context, "UPDATE 1\r\n", timeout=2)
-@then("we see data selected")
-def step_see_data_selected(context):
+@then("we see data selected: {data}")
+def step_see_data_selected(context, data):
"""
- Wait to see select output.
+ Wait to see select output with initial or updated data.
"""
+ x = UPDATED_DATA if data == "updated" else INITIAL_DATA
wrappers.expect_pager(
context,
dedent(
- """\
+ f"""\
+-----+\r
| x |\r
|-----|\r
- | yyy |\r
+ | {x} |\r
+-----+\r
SELECT 1\r
"""
@@ -102,6 +133,26 @@ def step_see_data_selected(context):
)
+@then("we see select output without data")
+def step_see_no_data_selected(context):
+ """
+ Wait to see select output without data.
+ """
+ wrappers.expect_pager(
+ context,
+ dedent(
+ """\
+ +---+\r
+ | x |\r
+ |---|\r
+ +---+\r
+ SELECT 0\r
+ """
+ ),
+ timeout=1,
+ )
+
+
@then("we see record deleted")
def step_see_data_deleted(context):
"""
@@ -116,3 +167,19 @@ def step_see_table_dropped(context):
Wait to see drop output.
"""
wrappers.expect_pager(context, "DROP TABLE\r\n", timeout=2)
+
+
+@then("we see transaction began")
+def step_see_transaction_began(context):
+ """
+ Wait to see transaction began.
+ """
+ wrappers.expect_pager(context, "BEGIN\r\n", timeout=2)
+
+
+@then("we see transaction rolled back")
+def step_see_transaction_rolled_back(context):
+ """
+ Wait to see transaction rollback.
+ """
+ wrappers.expect_pager(context, "ROLLBACK\r\n", timeout=2)
diff --git a/tests/features/steps/expanded.py b/tests/features/steps/expanded.py
index ac84c41..302cab9 100644
--- a/tests/features/steps/expanded.py
+++ b/tests/features/steps/expanded.py
@@ -16,7 +16,7 @@ def step_prepare_data(context):
context.cli.sendline("drop table if exists a;")
wrappers.expect_exact(
context,
- "You're about to run a destructive command.\r\nDo you want to proceed? (y/n):",
+ "You're about to run a destructive command.\r\nDo you want to proceed? [y/N]:",
timeout=2,
)
context.cli.sendline("y")
diff --git a/tests/features/steps/wrappers.py b/tests/features/steps/wrappers.py
index 6180517..3ebcc92 100644
--- a/tests/features/steps/wrappers.py
+++ b/tests/features/steps/wrappers.py
@@ -3,10 +3,7 @@ import pexpect
from pgcli.main import COLOR_CODE_REGEX
import textwrap
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
+from io import StringIO
def expect_exact(context, expected, timeout):