-
-
Notifications
You must be signed in to change notification settings - Fork 10
Comparing changes
Open a pull request
base repository: NpgsqlRest/NpgsqlRest
base: 3.18.2
head repository: NpgsqlRest/NpgsqlRest
compare: 3.19.0
- 14 commits
- 41 files changed
- 1 contributor
Commits on Jun 30, 2026
-
feat: SQL test runner (--test) [WIP], SqlFileSource SkipPattern, Mini…
…malLevels Off SQL test runner (--test) — WIP, not yet in changelog (still stabilizing): - Discovers *.test.sql, runs each on its own non-pooled connection, invokes endpoints in-process on the test's own connection/transaction, asserts via boolean SELECTs / DO blocks / `# @expect-status`. Captures responses into per-block pg_temp tables. - Per-assertion reporting (each boolean SELECT / DO block / expect-status HTTP step is one test); console grouped per file, JUnit one <testcase> per assertion. - Dedicated `NpgsqlRestTest` log channel (configurable TestRunner.LoggerName, leveled independently via Log:MinimalLevels): discovery/parsing at Debug, queries + HTTP calls at Verbose, notices by severity. Console report stays separate. - Core (additive, inert when unused): NpgsqlRestOptions.AmbientConnectionAccessor + connection-selection branch; RoutineInvokeResult.Headers; endpoint notice-handler leak fix (named delegate detached in finally); NpgsqlRestLogger.LogConnectionNotice ILogger overload. - 33 parser unit tests (NpgsqlRestTests/TestRunnerTests/ParserTests/). Shippable in 3.19.0 (changelog/v3.19.0.md): - SqlFileSource.SkipPattern (default *.test.sql) — exclude files from endpoint discovery; included iff matches FilePattern AND not SkipPattern. - Log:MinimalLevels accepts "Off"/"None"/"Silent" to fully mute a logger (minimum level above Fatal); null/omitted still falls back to the default. Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs. Full suite green (2334) + 33 parser + 94 config tests.
Configuration menu - View commit details
-
Copy full SHA for 6a0f03f - Browse repository at this point
Copy the full SHA 6a0f03fView commit details
Commits on Jul 2, 2026
-
feat(test-runner): test database support, per-assertion hardening, ch…
…angelog Test database / separate test connection: - TestRunner.ConnectionName: run tests (endpoint Describe + execution) against a named ConnectionStrings entry instead of the app's main connection; not opened/validated at startup, so a Setup step can create the database first. - Per-step ConnectionName on Setup/Teardown Sql/SqlFile steps (e.g. an Admin maintenance connection running `create database`); runner issues no DDL of its own. Steps now run in the exact order written (dropped Command-first grouping). BuildTestRunnerConnectionStrings resolves entries unvalidated. - {rnd1}..{rnd10} config placeholders: random lowercase tokens (length = digit), generated once per run, substituted everywhere {ENV} works — one stable name across connection string, create, and drop. Hardening round (full-feature review): - `# @expect-status` removed — assert on the response temp table's status column instead; HTTP blocks are pure act steps. - Code generation (HTTP files, TS client, OpenAPI) skipped in --test mode — a test run never rewrites generated artifacts. - login/logout endpoints rejected in test mode with a clear error (inject the principal with `# @claim` instead). - Request matching no endpoint logs a warning (path typo / missing /api prefix) — the 404 remains assertable. - Per-step exception attribution: errors thrown by HTTP steps (e.g. 42P07 duplicate `# @response` table) report with the block's file:line. Rename: TestRunner.Verbose -> DetailedReport (detailed console report: passed ✓ lines, full failing SQL, notices for passing tests) — removes the collision with the Serilog "Verbose" level in Log:MinimalLevels. changelog/v3.19.0.md rewritten: SQL test runner documented as the headline feature with a full user manual (execution model, file anatomy, HTTP-block syntax, response temp tables, Setup/Teardown, test-database workflow, reporting, logging, config reference), plus SqlFileSource.SkipPattern and MinimalLevels "Off". Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs. Full suite green (2334); examples 19 + 20 verified end-to-end (fresh {rnd}-named test DB created, migrated, tested, dropped).Configuration menu - View commit details
-
Copy full SHA for 0a20ec9 - Browse repository at this point
Copy the full SHA 0a20ec9View commit details -
feat(test-runner): script includes, named steps, per-file setup/teard…
…own/connection Script reuse — \i / \ir includes (psql syntax, paste semantics): - `\i path` (cwd-relative) / `\ir path` (relative to the including file) on its own line splices the included file's content in place, as if pasted: SQL statements, assertions (attributed to the included file), HTTP blocks (participate in _response_{n} numbering), and header annotations (a shared annotation "profile" attaches with one include line). - Recognized only between statements (never inside strings/dollar-quotes; no statement fragments — use functions/views). Nested, cycle-safe, depth-capped. Works in Setup/Teardown SqlFile steps. Errors report the included file's name and line. Quoted paths and a trailing ';' accepted; other backslash lines pass through to PostgreSQL untouched. Named steps (TestRunner.Steps): - name -> step dictionary (same idiom as ConnectionStrings/Profiles); Setup/Teardown arrays mix name references and inline step objects; unknown name is a configuration error (exit 3). Step logs show phase + name + resolved code: `setup step CreateTemplate: create database app_template_abc`. Per-file header annotations (leading -- comments, before the first statement): - `-- @setup Name [Name ...]` runs named steps before the file - `-- @teardown Name [Name ...]` runs after the file — always, best-effort, after the file's connection closes (so `drop database ... with (force)` works), on the run token (survives per-test timeout) - `-- @connection Name` runs the file, including its in-process endpoint calls, on a named ConnectionStrings entry - Repeatable; names whitespace- or comma-separated; accumulate and execute in written order (teardown not reversed); setup fail-fast, teardown best-effort. Together these enable per-test database isolation: clone a template, run in the clone, drop it (deterministic sequence ids). {rnd} indexed instances: - {rndN_1}..{rndN_9} are independent random tokens per length (all stable per run) for when several distinct same-length names are needed. Changelog updated (paste-model includes, named steps, per-file annotations, indexed tokens, example 21). Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs. Full suite green (2351) incl. 50 parser tests; examples 19/20/21 verified end-to-end (21: two parallel per-test clones from one template, shared annotation profile via \ir, Command step with {rnd} substitution).Configuration menu - View commit details
-
Copy full SHA for 58ae087 - Browse repository at this point
Copy the full SHA 58ae087View commit details -
feat(test-runner): filtering, watch mode, tags, endpoint coverage (Ph…
…ase 3) Test filtering (TestRunner.Filter): - Narrow the discovered set: a value without wildcards is a case-insensitive substring match against the cwd-relative path (--testrunner:filter=login); with wildcards it uses the same glob engine as FilePattern. Setup/Teardown still run; zero matches exits 4 (AllowEmpty applies). Watch mode (--watch / TestRunner.Watch, interactive/dev-only): - Setup once, run once, then watch the FilePattern base directory recursively for *.sql changes (debounced): a changed test file re-runs alone (Filter applies); any other changed .sql (an included fixture/profile) re-runs everything. Endpoint sources are not watched — restart to rebuild. - Teardown runs ONCE, on exit: SIGINT and SIGTERM are intercepted via PosixSignalRegistration (Console.CancelKeyPress proved unreliable headless; plain SIGTERM previously killed the process without teardown, leaking the test database). Graceful stop exits 0 — not for CI gating. - Extracted ExecuteDiscoveredFilesAsync (run core without teardown); RunAsync keeps teardown-in-finally for CI mode. Tags (-- @tag name [name ...] header annotation): - TestRunner.Tag runs only files carrying at least one listed tag; TestRunner.ExcludeTag skips files carrying any (exclude wins). CSV or whitespace-separated, case-insensitive, composes with Filter. Tags travel through profile includes (as-if-pasted), so e.g. every clone-isolated test is automatically tagged slow by its profile. Endpoint coverage (TestRunner.Coverage / CoverageThreshold): - After the run: exercised N of M testable endpoints + the list of untested ones (kinds the runner rejects — SSE, upload, login/logout, outbound proxy — are excluded from the ratio and counted separately). CoverageThreshold (0-100, implies Coverage) fails an otherwise-passing run with exit 2 — CI gating for "every endpoint has a test". Covered = invoked at least once. Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs; --watch whitelisted; --help updated. Changelog v3.19.0.md updated with all four features. Full suite green (2364) incl. 63 runner unit tests; examples 19/20/21 verified end-to-end (filter modes, watch rerun-on-change + signal teardown, tag taxonomies incl. profile-carried tags, coverage report + failing threshold gate).
Configuration menu - View commit details
-
Copy full SHA for bd4fed5 - Browse repository at this point
Copy the full SHA bd4fed5View commit details -
feat(test-runner): watch rebuilds endpoints, guaranteed teardown, the…
…med report Watch mode — endpoint source tree (SqlFileSource): - When the SQL file source is enabled, watch mode also watches its tree: a changed endpoint file triggers an in-process endpoint rebuild (UseNpgsqlRest re-reads + re-describes against the test database and atomically swaps the endpoint registry; a failed rebuild keeps the previous endpoints) followed by a full rerun. The endpoint delta is reported after each rebuild (+ METHOD path / - METHOD path "endpoint dropped - check its SQL file"), so breaking an endpoint mid-session is visible immediately and fixing it recovers - no restart. Watch mode forces SqlFileSource.ErrorMode from Exit to Skip (a broken file must not kill the session); non-watch --test keeps Exit for CI. Guaranteed teardown (fixes two database-leak classes): - From Setup onward, SIGINT (Ctrl+C) and SIGTERM are intercepted and Teardown runs SYNCHRONOUSLY in the signal handler - wrappers like `bun run` forward Ctrl+C to the process group and kill their children immediately, so the old cooperative unwind lost the race and leaked the test database. A second signal force-quits. Non-watch --test previously had no handler at all. - An AppDomain.ProcessExit hook covers hard exits: SqlFileSource ErrorMode=Exit's Environment.Exit(1) previously leaked the just-created test database; exit code unchanged, Teardown now runs first. - All paths funnel into a run-once teardown that hands every caller the SAME task (a fire-once no-op guard let the process exit mid-DROP). - An interrupted non-watch run exits 2. Console report themed to Serilog's Code theme: - FAIL/ERROR labels render as the byte-identical chip the Code theme uses for its ERR/FTL level (red-197 on grey-238); PASS uses the same chip grammar in the mirror green-47; line text stays in the terminal's normal color; ALL failure text unified on the theme's red-197 foreground (two stragglers used 16-color ConsoleColor.Red, which renders orange in some themes). Out.LineChip renders chips, plain when redirected. Changelog updated (endpoint watching, teardown guarantees, report theming, exit codes). Full suite green (2364); verified live: benign endpoint edit -> rebuild + green, broken endpoint -> dropped-endpoint delta + failing tests, fix -> recovery; broken-endpoint hard exit -> exit 1 with ZERO leaked databases; watch SIGTERM -> full teardown, zero leftovers.
Configuration menu - View commit details
-
Copy full SHA for 45a5e81 - Browse repository at this point
Copy the full SHA 45a5e81View commit details -
fix(test-runner): one red style, one green style in the console report
AnsiFail is now the full Serilog Code ERR chip style (red-197 on grey-238) and is the ONLY red in the report — FAIL/ERROR labels and every failure line (assertion details, messages, failing summary, coverage gate, setup/config errors) share the exact same escape sequence, so they can never render differently. AnsiOk is the mirror (green-47 on grey-238) for the PASS label and the all-green summary. Indentation and blank lines are written outside the styled block so the grey strip starts at the text.
Configuration menu - View commit details
-
Copy full SHA for 94667a9 - Browse repository at this point
Copy the full SHA 94667a9View commit details -
feat(test-runner): endpoint coverage on by default for full runs (tri…
…-state) TestRunner.Coverage is now tri-state: - null (the new default): report the endpoint-coverage summary after FULL runs — it costs one line and surfaces untested endpoints passively — but stay quiet when the run is narrowed by Filter/Tag (a deliberately partial run would just nag about endpoints its subset never touches); - true: always report, including narrowed runs; - false: never report. CoverageThreshold always reports and gates when set, regardless of Coverage or narrowing — it is an explicit ask for enforcement. Config synced (appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs — incl. previously missing schema descriptions for Coverage/CoverageThreshold). Changelog updated. Full suite green (2364); all five behaviors verified live (default-on full run, narrowed suppression, forced on, forced off, threshold reporting).
Configuration menu - View commit details
-
Copy full SHA for 19ecbae - Browse repository at this point
Copy the full SHA 19ecbaeView commit details -
feat(sql-file-source): named parameters (:name) in SQL files
SQL file endpoints can use named placeholders instead of positional $1..$N: select * from users where email = :email and age > :age; The placeholder IS the parameter: :email becomes API parameter "email" through the same NameConverter routine parameters use (:user_id -> userId), so @param annotations that existed only to NAME positional parameters become unnecessary. Claim mappings (ParameterNameClaimsMapping, @user_parameters) hook up by the placeholder's own name with zero annotations. Mechanics: statements are rewritten to native $N right after parsing — PostgreSQL never sees :name, so Describe, type inference, and runtime behavior are identical to positional files. The same name (case-insensitive, first spelling wins) maps to ONE parameter file-wide, including across statements in multi-command files. Token-aware rewriter: strings ('', ""), comments (nested /* */), and dollar-quoted bodies untouched; :: casts, := calls, and numeric slice bounds (a[1:3]) never match (variable slice bounds need a space: a[1 : n]). Mixing $N and :name in one file is rejected (ErrorMode semantics). Annotations match named parameters by name: `@param email default null` works as-is; `@param :email citext` is a Describe type hint; the new `@param email type is citext` form retypes WITHOUT rename (core ParameterHandler, delegates to the rename path as rename-to-same-name; also tolerates a leading ':' on the parameter reference). Typed-annotation detection for HTTP-client/composite types accepts the named form. `?` (JDBC style) considered and rejected: ?, ?|, ?& and @? are PostgreSQL's own jsonb/geometric operators — no rewriter can safely claim `?`; same reason the PG JDBC driver requires ?? escapes. Documented in the changelog. Tests: 22 rewriter unit tests + 7 end-to-end endpoint tests (auto-naming, repeated placeholder from one value, required-param matching, name-matched defaults, `type is` retype producing an unquoted JSON int, claim mapping by placeholder name, mixed-style rejection, multi-command cross-statement sharing). Full suite green (2393). Example 20's login.sql converted to :email/:password live (docs repo, committed separately). Changelog section + plugin README updated.Configuration menu - View commit details
-
Copy full SHA for 6cd7150 - Browse repository at this point
Copy the full SHA 6cd7150View commit details
Commits on Jul 3, 2026
-
feat(watch): server watch mode — --watch restarts the server on SQL/c…
…onfig changes --watch is now a two-mode flag: with --test it is the test runner's in-process watch (unchanged); WITHOUT --test it runs the SERVER under a watch supervisor. Supervisor/child design (same model as dotnet watch): the process spawns itself as a child server (marked via NPGSQLREST_WATCH_CHILD; value = the supervisor PID) and watches the SqlFileSource tree plus the configuration files. On a debounced change it stops the child gracefully and starts a fresh one — the child runs the completely normal server pipeline (discovery, describe, code generation, Kestrel), so dev is production behavior. The one relaxation: SqlFileSource ErrorMode is forced Exit->Skip in the watch child so a broken SQL file logs its error and drops only its own endpoint. Details: - Edits to files matching SqlFileSource.SkipPattern (test files) do NOT restart the server. - Child crash: the supervisor prints "server exited (code N) — waiting for file changes" and revives on the next relevant save (no crash-looping). - Graceful stop: SIGTERM via libc DllImport on Unix (Process.Kill is SIGKILL); hard process-tree kill on Windows (a dev server has no teardown needs). - Parent watchdog in the child: if the supervisor dies without stopping it (SIGKILL, wrapper runners like bun run), the child exits by itself — no orphan holding the port. - Spawn works from the AOT binary (Environment.ProcessPath) and under `dotnet NpgsqlRestClient.dll` (dotnet host re-invoked with the dll). - DOTNET_USE_POLLING_FILE_WATCHER=1 switches to a 1s polling scan for environments where file events don't cross the filesystem boundary (Docker Desktop bind mounts). - --watch without --test and without an enabled SqlFileSource exits with a clear error (nothing to watch). - WatchUtils extracts the shared watch primitives (base dir, debounce, pattern match); the test runner delegates to it, behavior identical. Plan B (in-process endpoint hot-swap via a dynamic EndpointDataSource, target 3.20) is designed in scrap/WATCH_HOT_SWAP_PLAN.md. Verified live across 12 scenarios: initial serve, add/break/fix endpoint file (broken file 404s while the rest keeps serving), test-file edit ignored, config-edit restart, child crash recovery, supervisor SIGKILL -> child self-exit (no orphans), SIGTERM graceful stop with port freed, polling mode, nothing-to-watch error, and test mode unaffected. Full suite green (2393). Changelog: watch documented as a single two-mode feature.
Configuration menu - View commit details
-
Copy full SHA for f859a4f - Browse repository at this point
Copy the full SHA f859a4fView commit details -
feat(watch): database routine polling + one Watch config section for …
…both flavors Watch mode now watches ALL endpoint sources in BOTH flavors. Routine-source endpoints (functions/procedures) have no files to watch, so watch mode polls the DATABASE — with perfect fidelity: RoutineSource.CreateFingerprintCommand runs the SAME discovery query the endpoint source uses (same Query property, same ten configured filter parameters — refactored into a shared command builder used by Read() too), wrapped in an order-independent server-side hash (select sum(hashtextextended(q::text,0)) from (<query>) q). If the hash changes, the discovered endpoints changed — by definition: create/replace/ drop/alter of functions and procedures (function bodies are in the result via pg_get_functiondef), GRANT/REVOKE, COMMENT ON (annotations), and changes to the composite types and tables used as parameter/return types. Anything the discovery query does not read — unrelated tables, temp objects, data — can never cause a spurious trigger. - WatchDbPoller (client): polls on a dedicated non-pooled connection, takes the actual configured RoutineSource instances from EndpointSources; skips cleanly when there are none. Rebaseline() prevents self-triggering. - Server watch: the child polls and exits with code 64 -> the supervisor respawns immediately ("database change detected — restarting"). Supervisor loop reworked so the pending change batch survives respawn iterations (no competing channel readers). Routines-only projects are now watchable: --watch is valid without a SQL file source when polling is active. - Test watch: the poller feeds a sentinel into the existing change channel -> endpoint rebuild + full rerun ("change detected (database)"); the runner re-baselines after every rerun so committed fixtures never re-trigger. Config consolidated into ONE top-level Watch section (TestRunner.Watch REMOVED — it was the same word with a different type and role): "Watch": { "Enabled": false, "DatabasePollingInterval": "2s" } --watch is the CLI shorthand for Watch:Enabled (--watch:enabled=true also works); the flavor is picked by --test. Polling accepts "2s"/"500ms"/"1m"/ seconds/hh:mm:ss, 0 disables, and deactivates automatically when RoutineOptions.Enabled is false. Config synced x4 + schema descriptions. Tests: WatchDbFingerprintTests proves the hash tracks the discovery result exactly (create/replace/comment/drop and used-type changes fire; temp objects and unrelated tables never do; nameSimilarTo filter wiring verified). Full suite green (2394). Live-verified: function created in psql serving ~2s later on a routines-only watch; replace -> new body; drop -> 404; alter table add column reshaping a `returns setof` endpoint ([{"id":1}] -> [{"id":1,"name":"ada"}]); unrelated table create/drop -> zero restarts; test-watch database rerun without loops; all four activation paths (server/test x flag/config). Changelog: watch section finalized.Configuration menu - View commit details
-
Copy full SHA for ba18c74 - Browse repository at this point
Copy the full SHA ba18c74View commit details -
feat(test-runner): ResponseTempTable.DebugTable — permanent response …
…debug mirror Captured responses live in per-connection temp tables that vanish with the test's rollback and connection close — impossible to examine in a query editor afterwards, and re-issuing the request from an .http file cannot reproduce a response that depended on the test's uncommitted transactional fixtures. New setting ResponseTempTable.DebugTable (default null = off): when set (e.g. "_responses_debug"), every captured response is ALSO mirrored into a PERMANENT table with that name, written on a separate autocommit pooled connection — immune to rollbacks and connection closes, append-only so parallel test files cannot collide. The temp-table semantics (assertions, naming, transactions) are completely unchanged. ONE table covers the whole run: each HTTP block adds one row with captured_at, test_file, block (that block's response-table name — Name, a MultiNamePattern ordinal, or the # @response name), method, path, status, body, content_type, headers, is_success. Recreated at the start of every run, so it always holds the LAST run. Enabling it prints a loud warning (debugging aid — do not enable in CI); mirror inserts are best-effort (a failure logs a warning, never fails the test). In the fresh-test-database workflow combine with Keep, or teardown drops the database and mirror. Note: the naive alternative ("Permanent": true on the response table) was rejected — parallel tests would collide on one permanent name, and CREATE TABLE is transactional, so the rollback would erase the table exactly when it was needed. Config synced (appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs). Full suite green (2394). Live-verified on example 19: all three block-naming schemes distinguishable in one query, including the response body of a rolled-back fixture; rerun leaves exactly the last run's rows. Changelog + docs updated.
Configuration menu - View commit details
-
Copy full SHA for 76f4f56 - Browse repository at this point
Copy the full SHA 76f4f56View commit details -
fix(config): sync ConfigTemplate with the trimmed DebugTable comment …
…in appsettings.json The DebugTable comment block was removed from appsettings.json (leaving a stray whitespace-only line) but ConfigTemplate.cs still carried it, so the Config_OutputMatchesAppsettingsJson byte-match test failed on CI. appsettings.json is the source of truth: the stray line is removed and the template now matches it exactly. The setting stays documented in the JSON schema description, the changelog, and the docs site.
Configuration menu - View commit details
-
Copy full SHA for 0ef7a2f - Browse repository at this point
Copy the full SHA 0ef7a2fView commit details -
feat(test-runner): Steps.Enabled + shipped disabled example steps; "m…
…s" interval support Every Setup/Teardown step now has an "Enabled" flag (default true). A disabled step is simply IGNORED wherever it is referenced — Setup/Teardown arrays or per-file -- @setup / -- @teardown annotations — skipped with a debug log line, never an error. Referencing an UNKNOWN name remains a loud configuration error; Enabled=false is the one sanctioned skip. The default configuration ships six DISABLED example steps in TestRunner:Steps showing every step property (Sql, SqlFile, Command, WorkingDirectory, ConnectionName, Enabled) across the typical scenarios: CreateTestDatabase / DropTestDatabase ({rnd5}-named database on an "Admin" connection), ApplySchema (SqlFile), RunMigrationTool (Command + WorkingDirectory), StartDockerPostgres / StopDockerPostgres. Instead of typing a step from scratch, copy one, adjust names/paths/connections, and flip Enabled to true. (Defaults are a copy-from reference visible in appsettings.json and --config output — not a runtime layer merged into explicit config files.) Also from the config-comment review: - ParseTestTimeout gained "ms" support ("500ms") — the Watch comments and changelog already promised it but the parser silently fell back to the default (verified: --watch:databasepollinginterval=500ms -> poll 0.5s). - ResponseTempTable section comment carries a compact one-line DebugTable description; FilePattern comment names both test layouts. Config synced x4 + schema (byte-match green). Full suite green (2394). Live-verified: disabled-referenced step skipped with debug line + run green; flipped on via CLI override -> step executes; custom user step names produce no validation warnings (example 20 clean). Changelog + docs updated.
Configuration menu - View commit details
-
Copy full SHA for 536b122 - Browse repository at this point
Copy the full SHA 536b122View commit details -
Configuration menu - View commit details
-
Copy full SHA for ef4adbe - Browse repository at this point
Copy the full SHA ef4adbeView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff 3.18.2...3.19.0