Skip to content

test(database): exercise #440 stdout-leak guard + SQL backfill for #351 admin group (closes #545, #546) - #579

Merged
cristim merged 4 commits into
feat/multicloud-web-frontendfrom
test/db-coverage-gaps
May 28, 2026
Merged

test(database): exercise #440 stdout-leak guard + SQL backfill for #351 admin group (closes #545, #546)#579
cristim merged 4 commits into
feat/multicloud-web-frontendfrom
test/db-coverage-gaps

Conversation

@cristim

@cristim cristim commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

Hardens two database regression tests that did not actually exercise the code they claimed to guard, plus a SQL-level backfill for the #351 admin-group assignment.

Fixes

Migration

New migration 000053_backfill_admin_group_ids (up + down). Verified 000053 is free on base (highest prior is 000052 from the MFA PR).

Test plan

Closes #545, #546.

Summary by CodeRabbit

  • Bug Fixes

    • Admin users with missing group assignments are now automatically detected and restored during database migrations.
  • Improvements

    • Admin operation logging now properly uses the logging system for better observability.
  • Tests

    • Added comprehensive integration tests to verify admin account recovery and logging behavior.

Review Change Stack

@cristim cristim added triaged Item has been triaged priority/p2 Backlog-worthy severity/low Minor harm urgency/this-quarter Within the quarter impact/internal Team-internal only effort/m Days type/bug Defect type/chore Maintenance / non-user-visible labels May 20, 2026
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18860b79-93a5-4422-ae8d-2f5c06924c03

📥 Commits

Reviewing files that changed from the base of the PR and between 83ba93a and 69ba7c4.

📒 Files selected for processing (6)
  • internal/database/postgres/migrations/000056_backfill_admin_group_ids.down.sql
  • internal/database/postgres/migrations/000056_backfill_admin_group_ids.up.sql
  • internal/database/postgres/migrations/backfill_admin_group_ids_test.go
  • internal/database/postgres/migrations/helpers_test.go
  • internal/database/postgres/migrations/migrate.go
  • internal/database/postgres/migrations/migrate_security_integration_test.go
💤 Files with no reviewable changes (6)
  • internal/database/postgres/migrations/000056_backfill_admin_group_ids.down.sql
  • internal/database/postgres/migrations/000056_backfill_admin_group_ids.up.sql
  • internal/database/postgres/migrations/migrate.go
  • internal/database/postgres/migrations/backfill_admin_group_ids_test.go
  • internal/database/postgres/migrations/helpers_test.go
  • internal/database/postgres/migrations/migrate_security_integration_test.go

📝 Walkthrough

Walkthrough

This PR adds SQL migration 000056 to backfill missing Administrators group UUIDs for admin users, redirects admin logging from stdout to stderr, and provides integration test coverage with new output-capture test helpers.

Changes

Admin Group ID Backfill and Logging Redirection

Layer / File(s) Summary
SQL migration 000056: admin group_ids backfill with idempotency test
internal/database/postgres/migrations/000056_backfill_admin_group_ids.up.sql, internal/database/postgres/migrations/000056_backfill_admin_group_ids.down.sql, internal/database/postgres/migrations/backfill_admin_group_ids_test.go
Migration 000056 up idempotently appends the Administrators group UUID to users.group_ids for admin users with NULL or empty arrays; it deduplicates via DISTINCT unnest, guards with EXISTS for Administrators group presence, and updates updated_at. Down migration is a no-op with explanatory comment. Integration test seeds drifted admin row, runs migrations with empty admin email to trigger SQL-only backfill, and validates idempotency on second run.
Test infrastructure: stdout and logger capture utilities
internal/database/postgres/migrations/helpers_test.go
New imports and two test helper functions: captureStdout redirects os.Stdout to a pipe and returns captured text via cleanup, captureLogOutput captures standard logger output to a buffer. Both enable assertions about which output stream receives specific messages.
Logging fix: migrate.go and integration test for stderr-not-stdout behavior
internal/database/postgres/migrations/migrate.go, internal/database/postgres/migrations/migrate_security_integration_test.go
Updates assignAdminGroupAndWarn to route "Backfilled group_ids" message via log.Printf (logger/stderr) instead of fmt.Printf (stdout), with inline comments documenting admin-account logging policy. Integration test TestAssignAdminGroup_BackfillLogsToStderr_NotStdout seeds drifted admin row, runs migrations with admin email to trigger backfill, and asserts "Backfilled" appears in logger but not stdout.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • LeanerCloud/CUDly#546: Directly addresses the issue by adding the idempotent SQL migration 000056 for backfilling admin group_ids and comprehensive integration test coverage beyond the Go-level approach.

Possibly related PRs

  • LeanerCloud/CUDly#520: Both PRs modify internal/database/postgres/migrations/migrate.go to switch migration/admin logging from fmt.Printf (stdout) to log.Printf (logger/stderr) and add tests asserting those log lines do not go to stdout.

  • LeanerCloud/CUDly#393: Both PRs address admin group_ids drift/backfill logic by adjusting ensureAdminUser/assignAdminGroupAndWarn repairs and covering with integration tests including "Backfilled" log output validation.

Poem

🐰 A migration hops in to mend the gap,
Where admin groups wandered from their map—
No-op rollback stands guard so neat,
While logs now whisper on stderr's beat!
Idempotent, tested, a cleanup complete! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the main changes: it references the stdout-leak guard fix (#440), SQL backfill for admin group (#351), and the two issues being closed (#545, #546).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/db-coverage-gaps

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim

cristim commented May 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/database/postgres/migrations/migrate_security_integration_test.go (1)

19-52: ⚡ Quick win

Consider consolidating duplicate capture helpers.

The captureStdoutIntegration and captureLogOutputIntegration functions are duplicates of captureStdout and captureLogOutput from migrate_security_test.go (shown in relevant snippets). Since both files are in package migrations_test and migrate_security_test.go has no build tag, those helpers should be available to this integration test file when running with -tags=integration.

Consider removing the duplicates here and reusing the originals, or moving all capture helpers to a shared location like helpers_test.go (similar to getMigrationsPath()).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/database/postgres/migrations/migrate_security_integration_test.go`
around lines 19 - 52, Duplicate test helpers captureStdoutIntegration and
captureLogOutputIntegration should be removed and the tests should reuse the
existing captureStdout and captureLogOutput helpers; either delete these two
functions from migrate_security_integration_test.go and import/use the originals
in migrate_security_test.go (they live in the same package migrations_test and
are available under -tags=integration), or move the shared helpers into a new
helpers_test.go so both files reference the same captureStdout and
captureLogOutput symbols. Ensure references in
migrate_security_integration_test.go are updated to call captureStdout and
captureLogOutput (or the new shared names) and remove the duplicated
implementations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/database/postgres/migrations/migrate_security_integration_test.go`:
- Around line 19-52: Duplicate test helpers captureStdoutIntegration and
captureLogOutputIntegration should be removed and the tests should reuse the
existing captureStdout and captureLogOutput helpers; either delete these two
functions from migrate_security_integration_test.go and import/use the originals
in migrate_security_test.go (they live in the same package migrations_test and
are available under -tags=integration), or move the shared helpers into a new
helpers_test.go so both files reference the same captureStdout and
captureLogOutput symbols. Ensure references in
migrate_security_integration_test.go are updated to call captureStdout and
captureLogOutput (or the new shared names) and remove the duplicated
implementations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 329edcfb-2237-4ab3-91ed-8a6de8d7b27d

📥 Commits

Reviewing files that changed from the base of the PR and between bc8831b and 83ba93a.

📒 Files selected for processing (5)
  • internal/database/postgres/migrations/000053_backfill_admin_group_ids.down.sql
  • internal/database/postgres/migrations/000053_backfill_admin_group_ids.up.sql
  • internal/database/postgres/migrations/backfill_admin_group_ids_test.go
  • internal/database/postgres/migrations/migrate.go
  • internal/database/postgres/migrations/migrate_security_integration_test.go

cristim added 4 commits May 28, 2026 15:10
The #440 stdout-leak fix routed the per-user admin messages to the stdlib
logger (stderr) but left the group_ids backfill line in assignAdminGroupAndWarn
on fmt.Printf, which writes to stdout. The existing unit regression test could
not catch it because it uses an unreachable pool, so the backfill branch never
runs.

- migrate.go: switch the "Backfilled ..." line from fmt.Printf to log.Printf so
  every admin-activity message in the file stays on the stderr-bound logger.
- Add integration regression test TestAssignAdminGroup_BackfillLogsToStderr_NotStdout
  that seeds a drifted admin against a real container, runs the real
  ensureAdminUser path, and asserts the backfill message lands on stderr and
  never on stdout. Verified to fail when the line is reverted to fmt.Printf.

Refs #545
… + test

Issue #351 acceptance criterion 2 asked for a migration-level idempotent
backfill. PR #533 backfilled only in Go (assignAdminGroupAndWarn), which fires
only when RunMigrations gets a non-empty admin email. A DB restored from a
backup, or migrated without ADMIN_EMAIL set, never re-applied the backfill to
pre-existing drifted admin rows. Migration 000024's backfill is one-shot at its
version and does not re-run on an already-migrated DB.

- Add migration 000053_backfill_admin_group_ids: the same idempotent backfill
  as 000024 (DISTINCT unnest dedupe, EXISTS guard, only touches empty group_ids
  so operator customisation is preserved), applied at migrate time regardless of
  how the deployment invokes migrations. Down is a documented no-op (additive
  backfill has no safe reverse).
- Add integration test TestMigration_BackfillAdminGroupIDs covering the
  restore / no-admin-email path: it runs migrations with NO admin email so the
  Go backfill cannot fire, proving the SQL migration repairs a drifted admin row,
  and asserts idempotency on re-apply. Verified to fail when 000053 is neutered.

The #351 group-assignment invariant already runs in default CI: ci.yml's
integration-tests job runs `go test -tags=integration ./...` against a postgres
service and is required by the ci-success gate, so TestEnsureAdminUser_GroupAssignment
is exercised on every PR.

Refs #546
… 000056

PR #614 merged 000053_executions_account_fk_restrict on the base branch
while this PR was open. Renumber the backfill migration to 000056 (next
free slot after 000055_add_paused_status) to clear the collision, and
update the test comment references to track the new number.

The migration files are renamed via `git mv` to preserve history. The
SQL contents are unchanged.
…est.go

Address CodeRabbit nitpick on PR #579: drop the duplicate
captureStdoutIntegration / captureLogOutputIntegration helpers from
migrate_security_integration_test.go and centralise the integration-tag
copies in helpers_test.go (same package, same build tag).

The duplication with migrate_security_test.go's captureStdout /
captureLogOutput is forced by a package boundary (that file lives in
`package migrations`, while integration tests live in `package
migrations_test`), so the helpers cannot be shared across files; the
new copies in helpers_test.go reuse the same names and carry a comment
pointing at the unit-test originals.
@cristim

cristim commented May 28, 2026

Copy link
Copy Markdown
Member Author

Pushed the consolidated capture helpers (69ba7c44f) addressing the nitpick from review 4349117639, plus the migration renumber to 000056 (985d17495) to stay out of feat's new range. migrate_security_test.go uses package migrations (internal) while helpers_test.go uses package migrations_test with the integration build tag, so there's no Go-level symbol clash between them.

@coderabbitai review

@cristim
cristim force-pushed the test/db-coverage-gaps branch from 83ba93a to 69ba7c4 Compare May 28, 2026 13:34
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

@cristim Thanks for the detailed update! Acknowledged:

  • Capture helpers (69ba7c44f): consolidated helpers addressing the previous nitpick.
  • Migration renumber to 000056 (985d17495): avoids overlap with the feat branch's new range.
  • Package separation: migrate_security_test.go (package migrations, internal) vs helpers_test.go (package migrations_test, integration build tag) — no symbol collision. Makes sense.

Triggering a fresh review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/internal Team-internal only priority/p2 Backlog-worthy severity/low Minor harm triaged Item has been triaged type/bug Defect type/chore Maintenance / non-user-visible urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant