pyln-testing: truncate long testnames
What changed, and why it matters
This change fixes a test-infrastructure bug in Core Lightning's Python testing helpers. When running tests against a PostgreSQL database, very long test names could be silently shortened by PostgreSQL, causing different test runs or nodes to accidentally share the same database name and fail with a 'DuplicateDatabase' error. The patch makes sure the unique parts of the name (node ID and random nonce) are kept while the long human-readable test name is shortened instead. It is a reliability fix for the test suite, not a security vulnerability in the live lightning node software.
No security action required. Treat as a normal test-framework bugfix. Reviewers may verify the new unit tests cover the truncation edge cases described in the commit message.
Security signals we found
No security-relevant signal: change is in test framework code only
Fixes a test reliability issue, not a runtime vulnerability
No input sanitization, authentication, cryptography, or network changes
No mention of security impact in commit message or changelog
Evidence from the diff
In contrib/pyln-testing/pyln/testing/db.py, PostgresDbProvider.get_db() previously constructed database names as ‘{testname}{node_id}’. PostgreSQL identifiers are truncated at 63 bytes (NAMEDATALEN - 1). For long test names this truncation could remove the node_id and/or nonce, causing name collisions and psycopg2 DuplicateDatabase errors. The patch introduces a db_name() helper that truncates testname while preserving the {node_id} suffix, and adds unit tests verifying length, distinctness per node, distinctness per nonce, and non-truncation of short names.
Changed components
contrib/pyln-testing/pyln/testing/db.pycontrib/pyln-testing/tests/test_dbname.pyInspect captured patch +59 / −1
### contrib/pyln-testing/pyln/testing/db.py
@@ -149,6 +149,9 @@ def stop(self) -> None:
class PostgresDbProvider(object):
+ # Postgres truncates identifiers at NAMEDATALEN - 1 bytes.
+ MAX_IDENTIFIER_LEN = 63
+
def __init__(self, directory):
self.directory = directory
self.port = None
@@ -223,10 +226,21 @@ def start(self):
# Required for CREATE DATABASE to work
self.conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
+ @staticmethod
+ def db_name(testname: str, node_id: int, nonce: str) -> str:
+ """Build the database name for a node of a test.
+
+ Truncates the testname, not the suffix: postgres would otherwise
+ cut off the node_id and the nonce, making the names of
+ long-named tests collide and fail with `DuplicateDatabase`.
+ """
+ suffix = "_{}_{}".format(node_id, nonce)
+ return testname[:PostgresDbProvider.MAX_IDENTIFIER_LEN - len(suffix)] + suffix
+
def get_db(self, node_directory, testname, node_id):
# Random suffix to avoid collisions on repeated tests
nonce = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8))
- dbname = "{}_{}_{}".format(testname, node_id, nonce)
+ dbname = self.db_name(testname, node_id, nonce)
cur = self.conn.cursor()
cur.execute("CREATE DATABASE {};".format(dbname))
### contrib/pyln-testing/tests/test_dbname.py
@@ -0,0 +1,44 @@
+from pyln.testing.db import PostgresDbProvider
+
+
+# Longest test name in the CLN test suite at the time of writing, at 66
+# characters it exceeds what postgres can store in an identifier.
+LONG_TESTNAME = "test_hsmtool_checkhsm_legacy_encrypted_with_mnemonic_no_passphrase"
+
+# Hardcoded rather than taken from PostgresDbProvider, so that changing
+# the limit in the provider fails these tests instead of silencing them.
+MAX_IDENTIFIER_LEN = 63
+
+
+def test_long_testname_fits_postgres_identifier():
+ name = PostgresDbProvider.db_name(LONG_TESTNAME, 1, "abcd1234")
+
+ assert len(name) <= MAX_IDENTIFIER_LEN
+
+
+def test_long_testname_keeps_node_id_and_nonce():
+ name = PostgresDbProvider.db_name(LONG_TESTNAME, 7, "abcd1234")
+
+ assert name.endswith("_7_abcd1234")
+
+
+def test_long_testname_distinct_per_node_after_truncation():
+ # Postgres silently truncates at 63 bytes, so names must already be
+ # distinct at that length or nodes of the same test collide.
+ names = {PostgresDbProvider.db_name(LONG_TESTNAME, i, "abcd1234")[:MAX_IDENTIFIER_LEN]
+ for i in range(5)}
+
+ assert len(names) == 5
+
+
+def test_long_testname_distinct_per_nonce_after_truncation():
+ n1 = PostgresDbProvider.db_name(LONG_TESTNAME, 1, "abcd1234")[:MAX_IDENTIFIER_LEN]
+ n2 = PostgresDbProvider.db_name(LONG_TESTNAME, 1, "wxyz5678")[:MAX_IDENTIFIER_LEN]
+
+ assert n1 != n2
+
+
+def test_short_testname_is_not_truncated():
+ name = PostgresDbProvider.db_name("test_peers", 1, "abcd1234")
+
+ assert name == "test_peers_1_abcd1234"Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.