Skip to content

fix(backend): reconcile scores table column names with ensure-core-tables migration (#1064) - #1681

Merged
blurbeast merged 3 commits into
LabsCrypt:mainfrom
solaawojobi00-bit:fix/issue-1064-scores-schema-mismatch
Aug 30, 2026
Merged

fix(backend): reconcile scores table column names with ensure-core-tables migration (#1064)#1681
blurbeast merged 3 commits into
LabsCrypt:mainfrom
solaawojobi00-bit:fix/issue-1064-scores-schema-mismatch

Conversation

@solaawojobi00-bit

Copy link
Copy Markdown
Contributor

Fix: Reconcile Scores Table Column Names with Core Migrations

Problem

Migration 1789000000000_ensure-core-tables.js aligns core tables and renames scores.user_id to borrower and scores.current_score to score. However, live queries across services (scoresService, scoreDecayService, scoreReconciliationService), controllers (scoreController, simulationController), and seed scripts continued to query user_id and current_score. On any migrated database, scoring operations failed on both reads and writes with column user_id does not exist or column current_score does not exist.

Scenarios

Scenario Pre-Fix Behavior Post-Fix Behavior
Fresh migrated database: get user score (GET /api/score/:userId) Fails with column current_score does not exist or column user_id does not exist Successfully queries score WHERE borrower = $1, falling back gracefully to 500 if unrecorded
Fresh migrated database: update score (POST /api/score/update) Upsert fails with missing column error on INSERT INTO scores (user_id, current_score) Upserts into scores (borrower, score) with ON CONFLICT (borrower) and returns updated score
Score decay background service (getInactiveBorrowers, applyScoreDecay) SQL query fails referencing s.user_id and s.current_score Queries and updates s.borrower and s.score correctly
Bulk score update / reconciliation (updateUserScoresBulk, setAbsoluteUserScoresBulk) Fails due to schema mismatch on conflict target and column names Atomically upserts rows targeting scores (borrower, score) and ON CONFLICT (borrower)
Database seeding (npm run seed:dev) Fails inserting seed records due to non-existent columns Seeds into scores (borrower, score, created_at) with conflict handling on borrower

Solution

  • Standardized the scores table column names to canonical schema: id, borrower, score, created_at, updated_at.
  • Updated all SQL queries across backend services, controllers, and seeds to target borrower and score.
  • Added defensive fallbacks (score ?? current_score) in controllers and reconciliation service to maintain compatibility during transitions.
  • Ensured migration 1789000000000_ensure-core-tables.js includes created_at on table creation and adds created_at if missing in the existing table branch.
  • Added a dedicated test in migration.test.ts exercising updateUserScoresBulk against the post-migration table schema.

Changes

backend/migrations/1789000000000_ensure-core-tables.js

  • Ensured created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP is specified in CREATE TABLE scores and added via ALTER TABLE if missing in the ELSE branch for idempotent migration.

backend/src/services/scoresService.ts

  • Updated updateUserScoresBulk to INSERT INTO scores (borrower, score) with ON CONFLICT (borrower) DO UPDATE SET score = ....
  • Updated setAbsoluteUserScoresBulk CTE and insert statement to use borrower and score.

backend/src/controllers/scoreController.ts

  • Updated getScore and updateScore queries to target score and borrower.
  • Updated getScoreBreakdown CTE query to select COALESCE(score, 500) AS current_score FROM scores WHERE borrower = $1.

backend/src/controllers/simulationController.ts

  • Updated getRemittanceHistory and simulatePayment to select score FROM scores WHERE borrower = $1.

backend/src/services/scoreDecayService.ts

  • Updated getInactiveBorrowers to SELECT s.borrower AS borrower, s.score AS score ... FROM scores s and applyScoreDecay to UPDATE scores SET score = $1 ... WHERE borrower = $2.

backend/src/services/scoreReconciliationService.ts

  • Updated fetchActiveBorrowerScores to SELECT DISTINCT a.address, s.score FROM active_loans a LEFT JOIN scores s ON s.borrower = a.address.

backend/src/services/eventIndexer.ts

  • Aligned commented reference query in _updateUserScore to borrower, score.

backend/src/seed/index.ts & backend/src/seed/data/users.ts

  • Updated seedScores to insert into scores (borrower, score, created_at) with ON CONFLICT (borrower).
  • Updated SeedUser interface and seed records to define borrower and score.

Tests

  • backend/src/__tests__/migration.test.ts: Added test asserting updateUserScoresBulk operates against the post-migration scores table.
  • backend/src/__tests__/scoresService.test.ts: Updated test table schema and test queries to use borrower and score.
  • backend/src/services/__tests__/scoresService.test.ts: Updated assertion checking for ON CONFLICT (borrower).
  • backend/src/services/__tests__/scoreDecayService.test.ts: Updated SQL string assertions for s.borrower, s.score, and WHERE borrower = $2.
  • backend/src/services/__tests__/scoreReconciliationService.test.ts, backend/src/__tests__/scoreReconciliationService.test.ts, backend/src/__tests__/simulationController.test.ts, backend/src/__tests__/score.test.ts: Updated mock DB records to provide score: ....

Regression Tests (Acceptance Criteria Mapping)

Acceptance Criterion Verification Method Status
Schema and code agree on one set of column names for scores Audited all SQL statements in backend/src; all target borrower and score Passed
scoresService, scoreController, and simulationController queries run against migrated schema Executed test suites covering scoresService, scoreController, and simulationController Passed
A test exercises updateUserScoresBulk against post-migration schema Added integration test should exercise updateUserScoresBulk against post-migration scores table in migration.test.ts Passed
Seed script updated to match seedScores in seed/index.ts and seedUsers in seed/data/users.ts updated and type-checked Passed
Lint, format, typecheck, and CI pass npm run typecheck, npm run build, npm run lint, npm run format:check, and npm test all green Passed

Testing (Literal Output)

Test Suites

PASS src/__tests__/scoreReconciliationService.test.ts
PASS src/__tests__/simulationController.test.ts
PASS src/services/__tests__/scoresService-additions.test.ts
PASS src/services/__tests__/scoresService.test.ts
PASS src/services/__tests__/scoreDecayService.test.ts
PASS src/__tests__/scoreBreakdown.test.ts
PASS src/__tests__/score.test.ts

Test Suites: 2 skipped, 8 passed, 8 of 10 total
Tests:       11 skipped, 56 passed, 67 total
Snapshots:   0 total
Time:        21.253 s

TypeScript Typecheck & Build

> [email protected] typecheck
> tsc -p tsconfig.build.json --noEmit

> [email protected] build
> tsc -p tsconfig.build.json

ESLint & Prettier

> [email protected] lint
> eslint .
✖ 35 problems (0 errors, 35 warnings)

> [email protected] format:check
> prettier --check .
All matched files use Prettier code style!

Notes for Reviewers

  • No data loss or breaking changes to Redis cache keys or score deltas.
  • Idempotent migration handling ensures databases created both before and after migration 1789000000000 have the required created_at column.

Closes #1064

@solaawojobi00-bit
solaawojobi00-bit force-pushed the fix/issue-1064-scores-schema-mismatch branch from d15ae01 to 4e675db Compare August 30, 2026 17:41
@blurbeast
blurbeast merged commit eb0b08b into LabsCrypt:main Aug 30, 2026
14 checks passed
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.

[Backend] ensure-core-tables migration renames scores.user_id/current_score but all scoring code still queries the old column names

2 participants