Skip to content

test: batch A — schema.ts mutation hardening (0% → 100%) - #110

Merged
Bonobo791 merged 1 commit into
mainfrom
mt-80-schema
Aug 5, 2026
Merged

test: batch A — schema.ts mutation hardening (0% → 100%)#110
Bonobo791 merged 1 commit into
mainfrom
mt-80-schema

Conversation

@Bonobo791

@Bonobo791 Bonobo791 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

User description

Behavior

First kill batch of the mutation-80% push (tracker: docs/mutation-80-backlog.md, baseline PR #108). src/lib/server/db/schema.ts was the worst file in the baseline: 170 survived / 0 killed (0.0%). After this PR: 137 killed, 0 survived, 33 ignored — 100.00%.

What changed

  • New src/lib/server/db/schema.test.ts (18 tests): asserts the full Drizzle schema shape via getTableConfig — table/column names, notNull, primary keys (composite + autoincrement), FKs with onDelete, unique constraints, indexes (incl. the partial consents retention index and its WHERE clause), the channels_org_requires_owner CHECK SQL, flag defaults, and the exact created_at default expression via SQLiteSyncDialect.sqlToQuery. Explicit assertions, no snapshots.
  • 31 equivalent-mutant exclusions in schema.ts: a StringLiteral "" on a column db name that equals the property key is a no-op — drizzle treats an empty name as falsy and falls back to the property key (verified by hand on drizzle-orm 0.45.2). Each carries a per-line // Stryker disable next-line StringLiteral directive with the reason; two directives share lines with .default('free') and also ignore that mutant (the default stays pinned by the shape test). Documented in the backlog triage log.
  • Backlog doc updated with the Batch A triage entry.

perTest coverage-attribution gotcha (recorded in the triage log)

The first scoped run killed only 21/170: Stryker's perTest coverage attributes module-top-level mutants only to tests that execute the module body, and a static import attributes everything to whichever test loads the module first. Re-importing the schema per test (vi.resetModules() + dynamic import(), the idiom from index.test.ts) fixed attribution → 139 killed. This also means the full-suite baseline undercounts kills for declarative module-scope code elsewhere.

Verification

  • npx stryker run --mutate "src/lib/server/db/schema.ts" --ignoreStatic100.00% (137 killed / 0 survived / 33 ignored); every kill is a survived→killed flip against the baseline run
  • npm run test — 551/551 pass
  • npm run check — 0 errors, 0 warnings
  • codacy-analysis analyze --files src/lib/server/db/schema.ts src/lib/server/db/schema.test.ts — 0 issues

Overall-score impact

schema.ts valid mutants: 170 → 137, all detected. Estimated overall: ~77.8% (from 72.76% baseline). Batches B–E continue per the backlog.


CodeAnt-AI Description

Lock down the database schema with comprehensive mutation tests

What Changed

  • Added explicit tests covering all database tables, columns, defaults, primary keys, foreign keys, cascading deletes, unique constraints, indexes, and tenancy rules
  • Verifies timestamp defaults, moderation flags, autoincrement IDs, composite keys, and the partial consent-retention index
  • Documented equivalent schema mutations so mutation testing reports no surviving valid mutations

Impact

✅ Fewer undetected database schema regressions
✅ Safer cascading deletes and tenant data separation
✅ Reliable defaults and indexes for stored data

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@cla-bot cla-bot Bot added the cla-signed label Aug 5, 2026
@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 9b44b8c Aug 05, 2026 · 17:35 17:38

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for moderaty ready!

Name Link
🔨 Latest commit 9b44b8c
🔍 Latest deploy log https://app.netlify.com/projects/moderaty/deploys/6a73746525b8440008d3c062
😎 Deploy Preview https://deploy-preview-110--moderaty.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 91
Accessibility: 97
Best Practices: 100
SEO: 100
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Tests

    • Added comprehensive database schema validation covering table structure, column behavior, keys, indexes, relationships, defaults, and constraints.
    • Expanded coverage to verify timestamp handling and conditional indexes across application tables.
  • Documentation

    • Updated mutation testing backlog records to document completed verification and reviewed exclusions.
  • Maintenance

    • Improved schema quality checks without changing application behavior or database runtime behavior.

Walkthrough

Changes

Schema Mutation Coverage

Layer / File(s) Summary
Schema contract tests
src/lib/server/db/schema.test.ts
Added metadata tests for all application tables, including defaults, keys, indexes, foreign keys, checks, timestamps, and retention behavior.
Stryker suppression directives
src/lib/server/db/schema.ts
Added suppressions for equivalent empty column-name mutations without changing schema behavior.
Batch A mutation triage record
docs/mutation-80-backlog.md
Marked Batch A complete and documented mutation results, test attribution, exclusions, and verification constraints.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Batch A schema mutation-hardening work and its reported outcome.
Description check ✅ Passed The description directly explains the schema tests, mutation exclusions, backlog update, and verification results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mt-80-schema

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 5, 2026
@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR adds metadata-driven tests that re-import the database schema and verify its tables, columns, constraints, indexes, defaults, and checks. Equivalent schema mutants are explicitly excluded while meaningful mutations are detected by the shape tests.

sequenceDiagram
    participant Tests
    participant Schema
    participant Drizzle

    Tests->>Schema: Re-import schema for each test
    Schema->>Drizzle: Define database tables and constraints
    Tests->>Drizzle: Read schema metadata
    Drizzle-->>Tests: Return table and constraint definitions
    Tests->>Tests: Verify names defaults keys and indexes
    Tests-->>Tests: Record mutation as killed or equivalent
Loading

Generated by CodeAnt AI

@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

@amazon-q-developer amazon-q-developer 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.

This PR achieves 100% mutation score for schema.ts (137 killed, 33 justified equivalents) through comprehensive schema validation tests. All changes are well-executed:

  • schema.test.ts: Thorough metadata-based validation using getTableConfig to verify table/column names, constraints, foreign keys, indexes, and defaults
  • schema.ts: Properly documented equivalent-mutant exclusions with clear justification
  • mutation-80-backlog.md: Accurate tracking of batch A completion

The PR includes successful verification (npx stryker run, all tests pass, npm run check, Codacy clean). Ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 9b44b8c9
Scan Time: 2026-08-05 17:37:59 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🔴 Metrics 59 complexity · 2 duplication

Metric Results
Complexity 59 (≤ 100 complexity)
Duplication ⚠️ 2 (≤ 1 duplication)

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Mutation testing: harden Drizzle schema contract to reach 100% detection

🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add explicit Drizzle schema-shape tests to kill all mutants in schema.ts.
• Mark equivalent StringLiteral mutants in schema.ts with justified Stryker disables.
• Update mutation-80 backlog with Batch A results and coverage-attribution notes.
Diagram

graph TD
  S([Stryker]) --> V([Vitest]) --> T["schema.test.ts"] --> ST["schema.ts"] --> D[["Drizzle ORM"]]
  T --> D
  B["mutation-80-backlog.md"] -->|"records results"| S

  subgraph Legend
    direction LR
    _tool(["Tool"]) ~~~ _file["File"] ~~~ _lib[["Library"]]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Snapshot the full getTableConfig output
  • ➕ Much faster to write and maintain as schema grows
  • ➕ Easier to diff when schema changes intentionally
  • ➖ Snapshots tend to be brittle/noisy and can hide which invariant broke
  • ➖ Harder to target specific mutation scenarios (e.g., partial index WHERE, check SQL)
2. Integration test via actual SQLite introspection (PRAGMA table_info/foreign_key_list/index_list)
  • ➕ Validates the real SQLite DDL outcome, not just Drizzle metadata
  • ➕ Catches ORM-to-SQL mismatches if they ever appear
  • ➖ More setup/IO; typically slower and more complex to maintain
  • ➖ PRAGMA outputs can vary and require more normalization than metadata assertions
3. Centralized schema invariant helper (iterate all tables)
  • ➕ Reduces repetition and keeps schema tests compact
  • ➕ Encourages consistent assertions across tables
  • ➖ Can reintroduce “testing the helper” risk and reduce explicitness that helps mutation kills
  • ➖ Less convenient for table-specific edge cases (partial index WHERE, named checks)

Recommendation: Keep the PR’s current approach: explicit, hand-written assertions against Drizzle metadata. For mutation testing, it provides targeted, deterministic failures (including named constraints, partial index WHERE SQL, and created_at default SQL) without snapshot noise. If the schema expands significantly, consider introducing small shared helpers (already started here) but avoid collapsing everything into opaque snapshots.

Files changed (3) +446 / -2

Tests (1) +376 / -0
schema.test.tsAdd Drizzle schema contract tests via getTableConfig metadata +376/-0

Add Drizzle schema contract tests via getTableConfig metadata

• Introduces a new Vitest suite that dynamically re-imports the schema per test to ensure module-scope mutants are attributed correctly. Asserts table/column shapes, PKs (including composite/autoincrement), foreign keys and onDelete behavior, unique constraints, indexes (including partial index WHERE), check constraint SQL, and created_at default SQL rendering.

src/lib/server/db/schema.test.ts

Documentation (1) +31 / -2
mutation-80-backlog.mdMark Batch A complete and document schema triage learnings +31/-2

Mark Batch A complete and document schema triage learnings

• Updates the batch plan to reflect Batch A completion with 100% detection for schema.ts. Adds a detailed triage log entry covering the new kill tests, Stryker perTest attribution gotcha, and equivalent-mutant justification guidance for later batches.

docs/mutation-80-backlog.md

Other (1) +39 / -0
schema.tsAnnotate equivalent StringLiteral mutants for schema column names +39/-0

Annotate equivalent StringLiteral mutants for schema column names

• Adds a file-level note explaining why empty-string column-name mutants are equivalent in Drizzle (fallback to property key). Applies targeted 'Stryker disable next-line StringLiteral' directives on affected columns to ignore equivalent mutants while relying on the new schema-shape tests to pin meaningful defaults and constraints.

src/lib/server/db/schema.ts

@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.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/lib/server/db/schema.test.ts`:
- Around line 64-65: Update expectColumns so the hasDefault assertion always
runs for every column, using false when shape.hasDefault is undefined. Preserve
explicitly provided hasDefault values while ensuring columns such as users.id,
sessions.expires_at, and channels.tone_level fail the test if they unexpectedly
have a default.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ac9929ac-6013-4e73-8558-8498d684979d

📥 Commits

Reviewing files that changed from the base of the PR and between 020be1b and 9b44b8c.

📒 Files selected for processing (3)
  • docs/mutation-80-backlog.md
  • src/lib/server/db/schema.test.ts
  • src/lib/server/db/schema.ts

Comment on lines +64 to +65
if (shape.hasDefault !== undefined)
expect(column.hasDefault, `${config.name}.${column.name} hasDefault`).toBe(shape.hasDefault);

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.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert hasDefault for every column, not only when declared.

expectColumns checks hasDefault only when the expectation supplies it. Columns such as users.id, sessions.expires_at, and channels.tone_level therefore have no hasDefault assertion. Default hasDefault to false so an unexpected default fails the test.

♻️ Proposed change
-		if (shape.hasDefault !== undefined)
-			expect(column.hasDefault, `${config.name}.${column.name} hasDefault`).toBe(shape.hasDefault);
+		expect(column.hasDefault, `${config.name}.${column.name} hasDefault`).toBe(
+			shape.hasDefault ?? false
+		);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (shape.hasDefault !== undefined)
expect(column.hasDefault, `${config.name}.${column.name} hasDefault`).toBe(shape.hasDefault);
expect(column.hasDefault, `${config.name}.${column.name} hasDefault`).toBe(
shape.hasDefault ?? false
);
🤖 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 `@src/lib/server/db/schema.test.ts` around lines 64 - 65, Update expectColumns
so the hasDefault assertion always runs for every column, using false when
shape.hasDefault is undefined. Preserve explicitly provided hasDefault values
while ensuring columns such as users.id, sessions.expires_at, and
channels.tone_level fail the test if they unexpectedly have a default.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Brittle schema SQL assertions 🐞 Bug ⚙ Maintainability
Description
schema.test.ts asserts exact SQL serialization output and ordered column lists from Drizzle
metadata, so semantically equivalent changes in Drizzle’s formatting/ordering can fail CI without
any actual schema contract change. This is especially likely to surface during routine drizzle-orm
minor bumps since the dependency is specified with a caret range.
Code

src/lib/server/db/schema.test.ts[R56-59]

+function expectColumns(table: SQLiteTable, expected: Record<string, ColumnShape>): void {
+	const config = getTableConfig(table);
+	expect(config.columns.map((c) => c.name)).toEqual(Object.keys(expected));
+	for (const column of config.columns) {
Relevance

●● Moderate

Team likes explicit schema/SQL pinning; but they’ve used order-insensitive column assertions
elsewhere.

PR-#3

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test helper expectColumns compares column names to Object.keys(expected) (order-sensitive),
and multiple tests compare sqlToQuery(...).sql output using toBe(...), which is
formatting-sensitive. The repo also allows drizzle-orm minor upgrades (^0.45.2), increasing the
chance of harmless formatting/order changes breaking tests.

src/lib/server/db/schema.test.ts[44-47]
src/lib/server/db/schema.test.ts[56-66]
src/lib/server/db/schema.test.ts[74-80]
src/lib/server/db/schema.test.ts[262-270]
src/lib/server/db/schema.test.ts[366-375]
package.json[33-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/lib/server/db/schema.test.ts` makes a few assertions that are sensitive to Drizzle internals (SQL serialization formatting and metadata ordering). This can break tests on semantically equivalent changes (e.g., whitespace/casing/quoting changes in `sqlToQuery`, or non-contractual reordering of `getTableConfig(...).columns`).

### Issue Context
These tests are intended to harden the schema contract, but not every assertion needs to be byte-for-byte stable. Where formatting/order is not part of the contract, the test should assert semantic equivalence instead.

### Fix Focus Areas
- src/lib/server/db/schema.test.ts[44-47]
- src/lib/server/db/schema.test.ts[56-59]
- src/lib/server/db/schema.test.ts[74-80]
- src/lib/server/db/schema.test.ts[262-270]
- src/lib/server/db/schema.test.ts[366-375]

### Suggested changes
- For column lists, compare unordered names when order is not explicitly contractual (e.g., sort both arrays before comparing, or compare as sets).
- For SQL text checks where formatting is not the contract, normalize before comparing (e.g., collapse whitespace and optionally standardize casing), or use `toMatch` with a precise regex that tolerates harmless formatting changes.
- Keep exact-string assertions only for cases where the emitted SQL text itself is intentionally pinned as part of the project’s schema contract.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 88 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +56 to +59
function expectColumns(table: SQLiteTable, expected: Record<string, ColumnShape>): void {
const config = getTableConfig(table);
expect(config.columns.map((c) => c.name)).toEqual(Object.keys(expected));
for (const column of config.columns) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Brittle schema sql assertions 🐞 Bug ⚙ Maintainability

schema.test.ts asserts exact SQL serialization output and ordered column lists from Drizzle
metadata, so semantically equivalent changes in Drizzle’s formatting/ordering can fail CI without
any actual schema contract change. This is especially likely to surface during routine drizzle-orm
minor bumps since the dependency is specified with a caret range.
Agent Prompt
### Issue description
`src/lib/server/db/schema.test.ts` makes a few assertions that are sensitive to Drizzle internals (SQL serialization formatting and metadata ordering). This can break tests on semantically equivalent changes (e.g., whitespace/casing/quoting changes in `sqlToQuery`, or non-contractual reordering of `getTableConfig(...).columns`).

### Issue Context
These tests are intended to harden the schema contract, but not every assertion needs to be byte-for-byte stable. Where formatting/order is not part of the contract, the test should assert semantic equivalence instead.

### Fix Focus Areas
- src/lib/server/db/schema.test.ts[44-47]
- src/lib/server/db/schema.test.ts[56-59]
- src/lib/server/db/schema.test.ts[74-80]
- src/lib/server/db/schema.test.ts[262-270]
- src/lib/server/db/schema.test.ts[366-375]

### Suggested changes
- For column lists, compare unordered names when order is not explicitly contractual (e.g., sort both arrays before comparing, or compare as sets).
- For SQL text checks where formatting is not the contract, normalize before comparing (e.g., collapse whitespace and optionally standardize casing), or use `toMatch` with a precise regex that tolerates harmless formatting changes.
- Keep exact-string assertions only for cases where the emitted SQL text itself is intentionally pinned as part of the project’s schema contract.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@Bonobo791
Bonobo791 merged commit 9c259d0 into main Aug 5, 2026
17 of 22 checks passed
@Bonobo791
Bonobo791 deleted the mt-80-schema branch August 5, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant