pgdevkit 0.6.0__tar.gz → 0.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/PKG-INFO +1 -1
  2. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/docs/database-layout.md +11 -0
  3. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/schema.py +23 -0
  4. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pyproject.toml +1 -1
  5. pgdevkit-0.6.1/tests/testdb/fixtures/database/app/tables/event.sql +10 -0
  6. pgdevkit-0.6.1/tests/testdb/fixtures/database/app/tables/event_kind.sql +4 -0
  7. pgdevkit-0.6.1/tests/testdb/fixtures/database/permissions/grants.sql +18 -0
  8. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_schema.py +28 -0
  9. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/uv.lock +1 -1
  10. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/.github/workflows/auto-release.yml +0 -0
  11. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/.github/workflows/python-publish.yml +0 -0
  12. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/.github/workflows/python-test.yml +0 -0
  13. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/.gitignore +0 -0
  14. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/.python-version +0 -0
  15. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/README.md +0 -0
  16. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/__init__.py +0 -0
  17. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/areas.py +0 -0
  18. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/backends/__init__.py +0 -0
  19. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/backends/base.py +0 -0
  20. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/backends/mssql.py +0 -0
  21. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/backends/postgres.py +0 -0
  22. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/cli.py +0 -0
  23. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/connection.py +0 -0
  24. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/__init__.py +0 -0
  25. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/complex_types.py +0 -0
  26. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/connection.py +0 -0
  27. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/crud.py +0 -0
  28. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/loader.py +0 -0
  29. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/model.py +0 -0
  30. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/mssql_crud.py +0 -0
  31. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/db/mssql_sql.py +0 -0
  32. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/dialect.py +0 -0
  33. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/diff.py +0 -0
  34. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/envtag.py +0 -0
  35. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/fetch_missing.py +0 -0
  36. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/introspect.py +0 -0
  37. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/lakebase.py +0 -0
  38. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/migrate.py +0 -0
  39. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/models.py +0 -0
  40. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/mssql_introspect.py +0 -0
  41. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/parser.py +0 -0
  42. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/schemas.py +0 -0
  43. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/sql_text.py +0 -0
  44. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/__init__.py +0 -0
  45. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/_docker.py +0 -0
  46. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/api.py +0 -0
  47. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/config.py +0 -0
  48. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/constants.py +0 -0
  49. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/container.py +0 -0
  50. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/mssql/__init__.py +0 -0
  51. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/mssql/api.py +0 -0
  52. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/mssql/constants.py +0 -0
  53. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/mssql/container.py +0 -0
  54. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/naming.py +0 -0
  55. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/pgdevkit/testdb/query.py +0 -0
  56. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/skills/pgdevkit/SKILL.md +0 -0
  57. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/skills/pgdevkit/references/dynamic-sql.md +0 -0
  58. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/skills/pgdevkit/references/temporal-tables.md +0 -0
  59. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/__init__.py +0 -0
  60. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/conftest.py +0 -0
  61. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/__init__.py +0 -0
  62. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_complex_types.py +0 -0
  63. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_connection.py +0 -0
  64. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_crud.py +0 -0
  65. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_loader.py +0 -0
  66. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_mssql_crud_live.py +0 -0
  67. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/db/test_mssql_crud_sql.py +0 -0
  68. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/01_schema.sql +0 -0
  69. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/02_types.sql +0 -0
  70. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/03_tables.sql +0 -0
  71. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/04_views.sql +0 -0
  72. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/05_functions.sql +0 -0
  73. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/fixtures/06_indexes.sql +0 -0
  74. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_areas.py +0 -0
  75. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_cli_compare.py +0 -0
  76. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_compare.py +0 -0
  77. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_compare_mssql_live.py +0 -0
  78. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_connection.py +0 -0
  79. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_dialect.py +0 -0
  80. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_diff_mssql.py +0 -0
  81. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_envtag.py +0 -0
  82. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_fetch_missing.py +0 -0
  83. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_fetch_missing_cli.py +0 -0
  84. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_lakebase.py +0 -0
  85. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_migrate.py +0 -0
  86. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_migrate_areas.py +0 -0
  87. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_migrate_env.py +0 -0
  88. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_migrate_schemas.py +0 -0
  89. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_mssql_introspect.py +0 -0
  90. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_parser_areas.py +0 -0
  91. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_parser_mssql.py +0 -0
  92. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_parser_schemas.py +0 -0
  93. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/test_schemas.py +0 -0
  94. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/__init__.py +0 -0
  95. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/conftest.py +0 -0
  96. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/migrations/001_add_gadget_note.sql +0 -0
  97. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/gadget.sql +0 -0
  98. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/gadget.test_data.json +0 -0
  99. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/prod_only.prod.sql +0 -0
  100. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/widget.init.sql +0 -0
  101. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/widget.sql +0 -0
  102. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/widget.test_data.json +0 -0
  103. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/widget_part.sql +0 -0
  104. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/tables/widget_part_detail.sql +0 -0
  105. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/types/dimensions.sql +0 -0
  106. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/types/mood.sql +0 -0
  107. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/views/a_wrapper_view.sql +0 -0
  108. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/app/views/b_base_view.sql +0 -0
  109. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database/schema/app.sql +0 -0
  110. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database_mssql/app/tables/widget.sql +0 -0
  111. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database_mssql/app/tables/widget.test_data.json +0 -0
  112. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database_mssql/app/views/a_wrapper_view.sql +0 -0
  113. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database_mssql/app/views/b_base_view.sql +0 -0
  114. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/fixtures/database_mssql/schema/app.sql +0 -0
  115. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_api.py +0 -0
  116. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_api_mssql.py +0 -0
  117. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_api_mssql_live.py +0 -0
  118. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_cli.py +0 -0
  119. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_config.py +0 -0
  120. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_config_mssql.py +0 -0
  121. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_constants.py +0 -0
  122. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_container.py +0 -0
  123. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_mssql_constants.py +0 -0
  124. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_naming.py +0 -0
  125. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_query.py +0 -0
  126. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_schema_filtering.py +0 -0
  127. {pgdevkit-0.6.0 → pgdevkit-0.6.1}/tests/testdb/test_schema_mssql.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pgdevkit
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: A helper for developing with Postgres
5
5
  Requires-Python: >=3.14
6
6
  Requires-Dist: docker>=7.1.0
@@ -63,6 +63,17 @@ both together.
63
63
  One object per file: `tables/user.sql`, `views/all_edits.sql`,
64
64
  `types/measurement_unit.sql`.
65
65
 
66
+ **`permissions` files always apply last, genuinely.** A blanket `GRANT ...
67
+ ON ALL TABLES IN SCHEMA x TO role;` only covers what exists at the moment it
68
+ runs — so if a table with a cross-file dependency that isn't resolved on the
69
+ first pass (e.g. an FK to a table in another layer directory whose filename
70
+ happens to sort later) got created *after* a `permissions` file, that grant
71
+ would silently never cover it. `pgdb testdb` holds every `permissions` file
72
+ back unconditionally and applies it only once the whole rest of the tree
73
+ (delayed-retry resolution included) has actually finished — so write
74
+ `permissions` files as if every table/view they reference is guaranteed to
75
+ already exist, because it is.
76
+
66
77
  ---
67
78
 
68
79
  ## File-naming conventions
@@ -132,6 +132,19 @@ def _iter_sql_files(
132
132
  ):
133
133
  """Yield (Path, sql_content) pairs in dependency-safe execution order.
134
134
 
135
+ Every `permissions`-type file (see `_TYPE_ORDER`) is held back
136
+ unconditionally and yielded only after every other file -- including
137
+ anything resolved via the delayed-retry loop below -- has actually been
138
+ delivered. A blanket "grant ... on all tables in schema X" has no real
139
+ per-object dependency of its own to track, but it still must apply
140
+ *after* every table/view it's meant to cover; a table whose own creation
141
+ got deferred to the retry loop (e.g. an FK to a same-type file that
142
+ happens to sort later) would otherwise silently miss that grant, since
143
+ a schema-wide GRANT is a one-time snapshot of whatever exists when it
144
+ runs. Nothing ever legitimately depends on a permissions file (it
145
+ creates no table/view/schema of its own), so holding every one back is
146
+ always safe.
147
+
135
148
  A file tagged for another environment (see pgdevkit.envtag) is dropped
136
149
  during the initial file walk, before dependency ordering even sees it.
137
150
 
@@ -154,6 +167,7 @@ def _iter_sql_files(
154
167
  delivered: set[str] = set()
155
168
  delayed: list[tuple[str | None, Path, str]] = []
156
169
  all_declared: set[str] = set()
170
+ permissions_files: list[tuple[Path, str]] = []
157
171
 
158
172
  for file in sorted(files, key=lambda p: (_get_type_order(p), p.name)):
159
173
  content = file.read_text(encoding="utf-8")
@@ -163,6 +177,9 @@ def _iter_sql_files(
163
177
  sql_schemas(content, dialect), only=schemas, exclude=exclude_schemas
164
178
  ):
165
179
  continue
180
+ if _get_type_order(file) == _TYPE_ORDER["permissions"]:
181
+ permissions_files.append((file, content))
182
+ continue
166
183
  deps = _get_sql_deps(content, dialect)
167
184
  if file.parent.name in _SCHEMA_QUALIFIED_TYPES:
168
185
  schema = _strip_layer_prefix(file.parent.parent.name)
@@ -196,6 +213,8 @@ def _iter_sql_files(
196
213
  if not progressed:
197
214
  raise ValueError(f"Circular or missing SQL dependencies: {[f[1] for f in delayed]}")
198
215
 
216
+ yield from permissions_files
217
+
199
218
 
200
219
  async def _insert_test_data(
201
220
  json_file: Path,
@@ -276,6 +295,10 @@ async def apply_schema(
276
295
  """Apply every .sql file under database_dir (in dependency-safe order)
277
296
  and seed any matching .test_data.json files. Safe to call repeatedly.
278
297
 
298
+ Every `permissions`-type file (see `_TYPE_ORDER`) is guaranteed to apply
299
+ after every table/view it's meant to cover, including anything resolved
300
+ via delayed-retry -- see `_iter_sql_files()`'s docstring.
301
+
279
302
  A file tagged for another environment (e.g. `grants.prod.sql` when
280
303
  `env="local_test"`) is skipped — see pgdevkit.envtag.
281
304
 
@@ -11,7 +11,7 @@ packages = ["pgdevkit"]
11
11
 
12
12
  [project]
13
13
  name = "pgdevkit"
14
- version = "0.6.0"
14
+ version = "0.6.1"
15
15
  description = "A helper for developing with Postgres"
16
16
  readme = "README.md"
17
17
  requires-python = ">=3.14"
@@ -0,0 +1,10 @@
1
+ -- Filename ("event.sql") deliberately sorts alphabetically before its own FK
2
+ -- target's filename ("event_kind.sql") -- '.' (0x2E) sorts before '_' (0x5F) --
3
+ -- so this file's first apply attempt always fails (app.event_kind doesn't
4
+ -- exist yet) and it only actually gets created via _iter_sql_files()'s
5
+ -- delayed-retry pass, which runs after every non-delayed file has already
6
+ -- been attempted. See test_apply_schema_applies_permissions_after_a_delayed_table.
7
+ CREATE TABLE IF NOT EXISTS app.event (
8
+ id serial PRIMARY KEY,
9
+ event_kind_id int NOT NULL REFERENCES app.event_kind(id)
10
+ );
@@ -0,0 +1,4 @@
1
+ CREATE TABLE IF NOT EXISTS app.event_kind (
2
+ id serial PRIMARY KEY,
3
+ name text NOT NULL
4
+ );
@@ -0,0 +1,18 @@
1
+ -- Roles are cluster-wide, not per-database, so this can race against another
2
+ -- pytest process (e.g. a second worktree) hitting the same shared testdb
3
+ -- container concurrently -- both can pass the IF NOT EXISTS check before
4
+ -- either commits. Catching duplicate_object (mirroring _ensure_database()'s
5
+ -- own DuplicateDatabase guard in pgdevkit/testdb/api.py) is race-safe in a
6
+ -- way "IF NOT EXISTS (SELECT ...) THEN CREATE" is not, since it lets
7
+ -- Postgres's own uniqueness check -- not a separate lookup -- be the source
8
+ -- of truth.
9
+ DO $$
10
+ BEGIN
11
+ CREATE ROLE pgdevkit_test_reader LOGIN PASSWORD 'testpwd';
12
+ EXCEPTION WHEN duplicate_object THEN
13
+ NULL;
14
+ END
15
+ $$;
16
+
17
+ GRANT USAGE ON SCHEMA app TO pgdevkit_test_reader;
18
+ GRANT SELECT ON ALL TABLES IN SCHEMA app TO pgdevkit_test_reader;
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
 
5
5
  import psycopg
6
6
  import pytest
7
+ from psycopg.conninfo import make_conninfo
7
8
 
8
9
  from pgdevkit.testdb import constants
9
10
  from pgdevkit.testdb.container import ensure_container
@@ -97,6 +98,33 @@ async def test_apply_schema_resolves_view_to_view_dependency(schema_test_db):
97
98
  assert rows == [(1, "sprocket")]
98
99
 
99
100
 
101
+ @requires_podman
102
+ async def test_apply_schema_applies_permissions_after_a_delayed_table(schema_test_db):
103
+ # permissions/grants.sql (a blanket "grant select on all tables in schema app")
104
+ # has no SQL dependencies of its own -- app.event, whose own filename
105
+ # ("event.sql") sorts alphabetically before its FK target's filename
106
+ # ("event_kind.sql"), always falls to the delayed-retry pass instead of
107
+ # resolving on the first attempt. Without holding permissions files back
108
+ # unconditionally, a blanket GRANT (a one-time snapshot of whatever exists
109
+ # when it runs) would apply before app.event exists and never cover it.
110
+ # Confirms _iter_sql_files() always yields permissions-type files last,
111
+ # genuinely after the whole tree (delayed retries included) has converged.
112
+ async with await psycopg.AsyncConnection.connect(_db_dsn(), autocommit=True) as con:
113
+ await apply_schema(con, FIXTURES) # must not raise
114
+
115
+ # permissions/grants.sql creates this login role itself -- a fixed,
116
+ # non-secret password, matching pgdevkit's own testdb container convention.
117
+ reader_dsn = make_conninfo(
118
+ host=constants.HOST, port=str(constants.PORT), dbname=TEST_DB,
119
+ user="pgdevkit_test_reader", password="testpwd",
120
+ )
121
+ async with await psycopg.AsyncConnection.connect(reader_dsn, autocommit=True) as con:
122
+ async with con.cursor() as cur:
123
+ await cur.execute("SELECT count(*) FROM app.event") # must not raise InsufficientPrivilege
124
+ (count,) = await cur.fetchone()
125
+ assert count == 0
126
+
127
+
100
128
  @requires_podman
101
129
  async def test_apply_schema_seeds_composite_enum_and_jsonb_columns(schema_test_db):
102
130
  # gadget.mood is an enum, gadget.size a composite type, gadget.tags
@@ -313,7 +313,7 @@ wheels = [
313
313
 
314
314
  [[package]]
315
315
  name = "pgdevkit"
316
- version = "0.6.0"
316
+ version = "0.6.1"
317
317
  source = { editable = "." }
318
318
  dependencies = [
319
319
  { name = "docker" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes