{"id":15067330,"url":"https://github.com/postgrespro/testgres","last_synced_at":"2026-02-13T00:19:26.969Z","repository":{"id":10451853,"uuid":"65575678","full_name":"postgrespro/testgres","owner":"postgrespro","description":"Testing framework for PostgreSQL and its extensions","archived":false,"fork":false,"pushed_at":"2026-01-20T17:39:11.000Z","size":1236,"stargazers_count":153,"open_issues_count":7,"forks_count":36,"subscribers_count":30,"default_branch":"master","last_synced_at":"2026-01-20T20:53:12.738Z","etag":null,"topics":["pathman","postgres","postgresql","replication","testgres","testing"],"latest_commit_sha":null,"homepage":"https://postgrespro.github.io/testgres","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/postgrespro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-08-12T18:36:30.000Z","updated_at":"2026-01-20T17:32:17.000Z","dependencies_parsed_at":"2026-01-20T14:01:14.637Z","dependency_job_id":null,"html_url":"https://github.com/postgrespro/testgres","commit_stats":{"total_commits":503,"total_committers":36,"mean_commits":"13.972222222222221","dds":0.5566600397614314,"last_synced_commit":"1cd6cd72ae761ec0b9f34ef8477c8b925de36965"},"previous_names":[],"tags_count":64,"template":false,"template_full_name":null,"purl":"pkg:github/postgrespro/testgres","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postgrespro%2Ftestgres","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postgrespro%2Ftestgres/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postgrespro%2Ftestgres/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postgrespro%2Ftestgres/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/postgrespro","download_url":"https://codeload.github.com/postgrespro/testgres/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postgrespro%2Ftestgres/sbom","scorecard":{"id":112976,"data":{"date":"2025-08-04","repo":{"name":"github.com/postgrespro/testgres","commit":"79bc692516ec4b2ed1ed3044e291b4a07a0fff5f"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"20 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Info: Possibly incomplete results: error parsing shell code: a command can only contain words and redirects; encountered (: Dockerfile--altlinux_10.tmpl:88","Info: Possibly incomplete results: error parsing shell code: a command can only contain words and redirects; encountered (: Dockerfile--altlinux_11.tmpl:88","Info: Possibly incomplete results: error parsing shell code: a command can only contain words and redirects; encountered (: Dockerfile--std-all.tmpl:36","Info: Possibly incomplete results: error parsing shell code: a command can only contain words and redirects; encountered (: Dockerfile--std2-all.tmpl:71","Warn: downloadThenRun not pinned by hash: Dockerfile--std2-all.tmpl:78","Warn: pipCommand not pinned by hash: publish_package.sh:13","Warn: pipCommand not pinned by hash: run_tests.sh:21","Warn: downloadThenRun not pinned by hash: run_tests.sh:62","Warn: pipCommand not pinned by hash: run_tests2.sh:27","Warn: downloadThenRun not pinned by hash: run_tests2.sh:68","Info:   0 out of   3 downloadThenRun dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 25 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-15T15:39:18.250Z","repository_id":10451853,"created_at":"2025-08-15T15:39:18.250Z","updated_at":"2025-08-15T15:39:18.250Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28917033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T16:37:38.804Z","status":"ssl_error","status_checked_at":"2026-01-30T16:37:37.878Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pathman","postgres","postgresql","replication","testgres","testing"],"created_at":"2024-09-25T01:19:40.742Z","updated_at":"2026-01-30T18:01:35.785Z","avatar_url":"https://github.com/postgrespro.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI Status](https://img.shields.io/github/actions/workflow/status/postgrespro/testgres/.github/workflows/package-verification.yml?label=CI)](https://github.com/postgrespro/testgres/actions/workflows/package-verification.yml)\n[![codecov](https://codecov.io/gh/postgrespro/testgres/branch/master/graph/badge.svg)](https://codecov.io/gh/postgrespro/testgres)\n[![PyPI package version](https://badge.fury.io/py/testgres.svg)](https://badge.fury.io/py/testgres)\n[![PyPI python versions](https://img.shields.io/pypi/pyversions/testgres)](https://pypi.org/project/testgres)\n[![PyPI downloads](https://img.shields.io/pypi/dm/testgres)](https://pypi.org/project/testgres)\n\n[Documentation](https://postgrespro.github.io/testgres/)\n\n# testgres\n\nUtility for orchestrating temporary PostgreSQL clusters in Python tests. Supports Python 3.7.3 and newer.\n\n## Installation\n\nInstall `testgres` from PyPI:\n\n```sh\npip install testgres\n```\n\nUse a dedicated virtual environment for isolated test dependencies.\n\n## Usage\n\n### Environment\n\n\u003e Note: by default `testgres` invokes `initdb`, `pg_ctl`, and `psql` binaries found in `PATH`.\n\nSpecify a custom PostgreSQL installation in one of the following ways:\n\n- Set the `PG_CONFIG` environment variable to point to the `pg_config` executable.\n- Set the `PG_BIN` environment variable to point to the directory with PostgreSQL binaries.\n\nExample:\n\n```sh\nexport PG_BIN=$HOME/pg_16/bin\npython my_tests.py\n```\n\n### Examples\n\nCreate a temporary node, run queries, and let `testgres` clean up automatically:\n\n```python\n# create a node with a random name, port, and data directory\nwith testgres.get_new_node() as node:\n\n    # run initdb\n    node.init()\n\n    # start PostgreSQL\n    node.start()\n\n    # execute a query in the default database\n    print(node.execute('select 1'))\n\n# the node is stopped and its files are removed automatically\n```\n\n### Query helpers\n\n`testgres` provides four helpers for executing queries against the node:\n\n| Command | Description |\n|---------|-------------|\n| `node.psql(query, ...)` | Runs the query via `psql` and returns a tuple `(returncode, stdout, stderr)`. |\n| `node.safe_psql(query, ...)` | Same as `psql()` but returns only `stdout` and raises if the command fails. |\n| `node.execute(query, ...)` | Connects via `psycopg2` or `pg8000` (whichever is available) and returns a list of tuples. |\n| `node.connect(dbname, ...)` | Returns a `NodeConnection` wrapper for executing multiple statements within a transaction. |\n\nExample of transactional usage:\n\n```python\nwith node.connect() as con:\n    con.begin('serializable')\n    print(con.execute('select %s', 1))\n    con.rollback()\n```\n\n### Logging\n\nBy default `cleanup()` removes all temporary files (data directories, logs, and so on) created by the API. Call `configure_testgres(node_cleanup_full=False)` before starting nodes if you want to keep logs for inspection.\n\n\u003e Note: context managers (the `with` statement) call `stop()` and `cleanup()` automatically.\n\n`testgres` integrates with the standard [Python logging](https://docs.python.org/3/library/logging.html) module, so you can aggregate logs from multiple nodes:\n\n```python\nimport logging\n\n# write everything to /tmp/testgres.log\nlogging.basicConfig(filename='/tmp/testgres.log')\n\n# enable logging and create two nodes\ntestgres.configure_testgres(use_python_logging=True)\nnode1 = testgres.get_new_node().init().start()\nnode2 = testgres.get_new_node().init().start()\n\nnode1.execute('select 1')\nnode2.execute('select 2')\n\n# disable logging\ntestgres.configure_testgres(use_python_logging=False)\n```\n\nSee `tests/test_simple.py` for a complete logging example.\n\n### Backup and replication\n\nCreating backups and spawning replicas is straightforward:\n\n```python\nwith testgres.get_new_node('master') as master:\n    master.init().start()\n\n    with master.backup() as backup:\n        replica = backup.spawn_replica('replica').start()\n        replica.catchup()\n\n        print(replica.execute('postgres', 'select 1'))\n```\n\n### Benchmarks\n\nUse `pgbench` through `testgres` to run quick benchmarks:\n\n```python\nwith testgres.get_new_node('master') as master:\n    master.init().start()\n\n    result = master.pgbench_init(scale=2).pgbench_run(time=10)\n    print(result)\n```\n\n### Custom configuration\n\n`testgres` ships with sensible defaults. Adjust them as needed with `default_conf()` and `append_conf()`:\n\n```python\nextra_conf = \"shared_preload_libraries = 'postgres_fdw'\"\n\nwith testgres.get_new_node().init() as master:\n    master.default_conf(fsync=True, allow_streaming=True)\n    master.append_conf('postgresql.conf', extra_conf)\n```\n\n`default_conf()` is called by `init()` and rewrites the configuration file. Apply `append_conf()` afterwards to keep custom lines.\n\n### Remote mode\n\nYou can provision nodes on a remote host (Linux only) by wiring `RemoteOperations` into the configuration:\n\n```python\nfrom testgres import ConnectionParams, RemoteOperations, TestgresConfig, get_remote_node\n\nconn_params = ConnectionParams(\n    host='example.com',\n    username='postgres',\n    ssh_key='/path/to/ssh/key'\n)\nos_ops = RemoteOperations(conn_params)\n\nTestgresConfig.set_os_ops(os_ops=os_ops)\n\ndef test_basic_query():\n    with get_remote_node(conn_params=conn_params) as node:\n        node.init().start()\n        assert node.execute('SELECT 1') == [(1,)]\n```\n\n### Pytest integration\n\nUse fixtures to create and clean up nodes automatically when testing with `pytest`:\n\n```python\nimport pytest\nimport testgres\n\n@pytest.fixture\ndef pg_node():\n    node = testgres.get_new_node().init().start()\n    try:\n        yield node\n    finally:\n        node.stop()\n        node.cleanup()\n\ndef test_simple(pg_node):\n    assert pg_node.execute('select 1')[0][0] == 1\n```\n\nThis pattern keeps tests concise and ensures that every node is stopped and removed even if the test fails.\n\n### Scaling tips\n\n- Run tests in parallel with `pytest -n auto` (requires `pytest-xdist`). Ensure each node uses a distinct port by setting `PGPORT` in the fixture or by passing the `port` argument to `get_new_node()`.\n- Always call `node.cleanup()` after each test, or rely on context managers/fixtures that do it for you, to avoid leftover data directories.\n- Prefer `node.safe_psql()` for lightweight assertions that should fail fast; use `node.execute()` when you need structured Python results.\n\n## Authors\n\n[Ildar Musin](https://github.com/zilder)  \n[Dmitry Ivanov](https://github.com/funbringer)  \n[Ildus Kurbangaliev](https://github.com/ildus)  \n[Yury Zhuravlev](https://github.com/stalkerg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpostgrespro%2Ftestgres","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpostgrespro%2Ftestgres","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpostgrespro%2Ftestgres/lists"}