Skip to content

feat: case-insensitive, whitespace-normalized name handles#6

Merged
RagingRedRiot merged 1 commit into
mainfrom
feat/case-insensitive-name-handles
May 30, 2026
Merged

feat: case-insensitive, whitespace-normalized name handles#6
RagingRedRiot merged 1 commit into
mainfrom
feat/case-insensitive-name-handles

Conversation

@RagingRedRiot

Copy link
Copy Markdown
Owner

Why

For a name to work as a lookup handle it has to be unique and consistently normalized — otherwise one human-entered name can resolve to zero rows or to several.

This PR establishes that contract for usernames now and lays the schema groundwork so rooms benefits when room.rs lands. It contains no room-actor code itself — just the normalization rules and the room_name schema those queries will depend on.

What changed

  • Case-insensitive uniqueness via LOWER() functional indexes on username and room_name. "Alice" and "alice" can't coexist, and a lookup resolves regardless of the casing the client sends. Stored casing is preserved for display.
  • trim_ws() — a SQL helper that strips all leading/trailing whitespace (space, tab, newline, CR, …), unlike bare TRIM, which only removes spaces. Routed through every write and lookup so padded input can't create near-duplicate or unreachable rows.
  • Empty → NULL for optional fields (first_name/last_name/alias): blank input collapses to NULL. On edit, an absent field is left can't be blanked.
  • CHECK (col <> '') on every text column (users, rooms, messages) as a hard backstop, so '' can never be stored via any code path — present or future.

Writes and lookups go through LOWER() / trim_ws() consistently so they still hit the functional indexes.

Testing

  • New tests/normalization.rs (16 tests): case-insensitive uniqueness + auth + lookup, casing preserved on read, whitespace-complete trimming (incl. tab/newline-only → rejected), empty→NULL on create, and the three-way edit semantics (none / blank / value).
  • Full suite green: 85 passed.

Notes for reviewers

  • tests/edit_user.rs has one intentional existing-test change: the empty_string_fields_overwrite_existing_values canary now expects None instead of Some(""), matching the new empty→NULL contract (its own comment predicted this). The CI test-modification check will flag it — that flag is expected here.
  • room_name's index and CHECK are added ahead of room.rs so the room actor can rely on them; carry the same trim_ws($1) / LOWER(room_name) = LOWER(trim_ws($1)) convention into those queries.

For a name to work as a lookup handle it must be unique and consistently normalized. This establishes that contract for usernames now and lays the schema groundwork so rooms inherit it when room.rs is complete.

- Case-insensitive uniqueness via LOWER() functional indexes on username and room_name: "Alice" and "alice" can't coexist, and a lookup resolves regardless of the casing the client sends. Original casing is preserved for display.
- trim_ws(): a SQL helper stripping all leading/trailing whitespace (space, tab, newline, ...), unlike bare TRIM which only removes spaces. Applied on every write and lookup so padded input can't produce near-duplicate or unreachable rows.
- Optional fields (first/last/alias) collapse empty/whitespace input to NULL. On edit, an absent field is left unchanged while an explicit blank clears it; a username can't be blanked.
- CHECK (col <> '') on every text column (users, rooms, messages) as a backstop, so an empty string can never be stored via any code path.

Writes and lookups route through LOWER()/trim_ws() consistently so they still use the functional indexes. room_name's index and CHECK are added ahead of room.rs so the room actor's queries can rely on them directly.
@github-actions

Copy link
Copy Markdown

⚠️ Existing tests were modified

The following test files have removed or changed lines compared to main:

  • tests/edit_user.rs (7 line(s) removed or changed)

Adding new tests is fine and does not trigger this notice. This appears when existing test code (or helpers under tests/common/) is edited or removed. That can be legitimate — but every change should be deliberate and visible to reviewers.

@RagingRedRiot RagingRedRiot merged commit a82b999 into main May 30, 2026
4 checks passed
@RagingRedRiot RagingRedRiot deleted the feat/case-insensitive-name-handles branch May 30, 2026 01:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant