Skip to content

Add unit tests for admin, build-log, measure and translation controllers (#686)#693

Merged
wheelsandcogs merged 2 commits into
mainfrom
worktree-issue-686-controller-tests
Jun 2, 2026
Merged

Add unit tests for admin, build-log, measure and translation controllers (#686)#693
wheelsandcogs merged 2 commits into
mainfrom
worktree-issue-686-controller-tests

Conversation

@wheelsandcogs

@wheelsandcogs wheelsandcogs commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds unit tests for the four under-covered controllers flagged in #686, bringing each well above the 70% statement-coverage target, and fixes two bugs the tests surfaced.

Controller Before After
src/controllers/admin.ts 23.1% 97.5%
src/controllers/build-log.ts 34.2% 100%
src/controllers/measure.ts 19.9% 92.4%
src/controllers/translation.ts 21.8% 92.1%

Both happy and unhappy paths are exercised, including the authorization / validation guards (uuid validation, status validation, by validation, unique-constraint handling, av-scan failures, missing-measure / missing-revision guards).

Bugs found and fixed

Writing the tests surfaced two real defects, each fixed here with a one-line return. The tests that caught them now pass and act as regression guards.

Bug 1 — admin.ts loadUserGroup and loadUser called next() twice on the not-found path

} catch (_err) {
  next(new NotFoundException('errors.no_user_group'));
  return; // <-- added
}
next(); // only runs on the success path now

Without the return the catch fell through to the unconditional next() at the end of the function, invoking next twice (once with the error, once without) and letting the request continue to the route handler with nothing loaded in res.locals — the same "responds twice" class of bug fixed in 95adb9db. Now matches the canonical datasetAuth middleware (src/middleware/dataset-auth.ts).

Bug 2 — measure.ts updateMeasureMetadata fell through its missing-measure guard

const measure = dataset.measure;
if (!measure) {
  next(new NotFoundException('errors.measure_invalid'));
  return; // <-- added
}
... measure.metadata.find(...) // previously a TypeError on the null measure

Without the return, execution dereferenced the null measure and threw a TypeError instead of returning a clean 404. Now matches getPreviewOfMeasure / getMeasureInfo.

Regression tests:

  • admin.loadUserGroup › calls next exactly once with NotFound when the group cannot be loaded
  • admin.loadUser › calls next exactly once with NotFound when the user cannot be loaded
  • measure.updateMeasureMetadata › forwards NotFound and does not throw when the dataset has no measure

Scope

This PR covers the four controllers from #686. The three services (dataset, revision, task) are handled separately in #692. The jest.config.ts coverage thresholds are left unchanged here; raising them is best done once both PRs land.

Verification

  • Per-controller coverage measured via jest --coverage (figures above).
  • Full suite: 1546 passing, 0 failing.
  • eslint and tsc --noEmit clean.

Part of #686.

…ers (#686)

Brings the four under-covered controllers up to the testing-initiative
target, exercising both happy and unhappy paths and the authorization /
validation guards on each:

- admin.ts:       23% -> 97.5% statements
- build-log.ts:   34% -> 100%  statements
- measure.ts:     20% -> 92.3% statements
- translation.ts: 22% -> 92.1% statements

Three tests are intentionally left FAILING because they document real
defects found while writing the tests (see PR description). They assert
the correct behaviour and are expected to go red until the bugs are
fixed:

1. admin.loadUserGroup / admin.loadUser call next() twice on the
   not-found path (missing return in the catch block), unlike the
   canonical datasetAuth middleware which returns after next(error).
2. measure.updateMeasureMetadata is missing a return after
   next(NotFoundException) when the dataset has no measure, so it falls
   through and dereferences the null measure.

Tests only - no source changes.
Copilot AI review requested due to automatic review settings June 2, 2026 10:09

Copilot AI 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.

Pull request overview

Adds Jest unit tests for four controllers (admin, build-log, measure, translation) flagged as under-covered by #686, raising each well above the 70% statement-coverage target. No source changes are included. Three tests are intentionally left red to document two real next()-after-guard bugs (admin.loadUserGroup/loadUser and measure.updateMeasureMetadata), per the PR author's explicit design choice.

Changes:

  • New test file test/unit/controllers/admin.test.ts exercising group/user CRUD, role/status validation, dashboard, similar-dataset reports, and search-log export — including two intentionally-failing tests for the double-next() bug in loadUserGroup / loadUser.
  • New test file test/unit/controllers/measure.test.ts covering measure info/preview/reset/metadata-update and lookup-table attach/download paths — including one intentionally-failing test for the missing return after the no-measure guard in updateMeasureMetadata.
  • New test files test/unit/controllers/build-log.test.ts and test/unit/controllers/translation.test.ts covering paging/validation, CSV streaming, av-scan failures, and cube-rebuild failures.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
test/unit/controllers/admin.test.ts Full controller coverage; includes 2 red tests asserting single next() invocation in loadUserGroup / loadUser.
test/unit/controllers/measure.test.ts Covers CRUD + lookup-table flows; includes 1 red test asserting updateMeasureMetadata returns after the no-measure NotFoundException.
test/unit/controllers/build-log.test.ts Default/parameterised paging, type/status validation errors, and not-found paths for getBuiltLogEntry.
test/unit/controllers/translation.test.ts Preview, export, validate-import (row-count and key mismatches), and apply-import including cube-rebuild failure.

…trollers

Add the missing `return` after next(error) in three controller guards
that the new tests surfaced:

- admin.loadUserGroup / admin.loadUser: the catch block fell through to
  the unconditional next() at the end of the function, calling next()
  twice on the not-found path. Return after next(error), matching the
  canonical datasetAuth middleware.
- measure.updateMeasureMetadata: the missing-measure guard fell through
  and dereferenced the null measure (TypeError). Return after
  next(NotFoundException), matching getPreviewOfMeasure / getMeasureInfo.

The three tests added in the previous commit now pass and serve as
regression guards.

@j-maynard j-maynard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM 🤘🏻

@wheelsandcogs wheelsandcogs merged commit d7c8a19 into main Jun 2, 2026
6 checks passed
@wheelsandcogs wheelsandcogs deleted the worktree-issue-686-controller-tests branch June 2, 2026 10:51
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.

3 participants