Skip to content

test: add unit tests to improve coverage for 9 core packages - #11855

Open
pranesh-sridhar wants to merge 7 commits into
loopbackio:masterfrom
pranesh-sridhar:test/model-api-builder-http-caching-proxy-unit-coverage
Open

pranesh-sridhar wants to merge 7 commits into
loopbackio:masterfrom
pranesh-sridhar:test/model-api-builder-http-caching-proxy-unit-coverage

Conversation

@pranesh-sridhar

Copy link
Copy Markdown

Add unit tests to cover code paths in nine core packages that the existing suites did not exercise. Gaps were identified from measured nyc coverage of each package's dist/ output. Only test files are changed (plus model-api-builder's package.json and tsconfig.json to wire up the test script and devDependencies, as no tests existed there at all).

Coverage summary

Package Tests before Tests after Statements before Statements after
model-api-builder 0 5 0% (no test script) 100%
http-caching-proxy 13 22 93.84% 98.46% (branches 55% → 94%)
openapi-spec-builder 27 50 88.60% 100% statements/branches/functions/lines
rest-crud 19 26 68.23% 100% statements/lines
filter 212 216 96.83% 99.36% (branches 87% → 97%)
repository-json-schema 161 165 95.48% 98.87%
repository (types) — +7 object.ts 68%, buffer.ts 95% br both 100%
rest — +16 redirect-route 71% stmts 100% stmts/lines
tsdocs 4 7 86.39% 88.43% (helper.ts unscoped branch covered)

Tests added

  • model-api-builder: asModelApiBuilder binding template (extensionFor tag, namespace tag, tagNames, key unchanged), MODEL_API_BUILDER_PLUGINS constant
  • http-caching-proxy: constructor guard, stop() no-op when not running, logError() with logError:true/false, error.stack present/absent branches; integration test for non-ENOENT cache corruption (EINTEGRITY) falling back to backend
  • openapi-spec-builder: withOperationReturningString with and without operationName, withOperation accepting an OperationSpecBuilder, withComponents accepting a ComponentsSpecBuilder, withComponents idempotency (??=), withRequestBody, withStringResponse default status, setupOperationId not overwriting an existing id, all nine ComponentsSpecBuilder methods called twice to hit the ??= already-initialised branch
  • rest-crud: CrudRestComponent registers binding; CrudRestApiBuilder.build() — missing basePath error, non-Entity model error, creates repo+controller when absent, reuses existing repo binding, registers readonly controller
  • filter: WhereBuilder.impose and FilterBuilder.impose with falsy internal state (defensive !this.where/!this.filter branches that are unreachable via the public API but reachable via direct property mutation)
  • repository-json-schema: getNavigationalPropertyForRelation with targetsMany:false (returns targetRef) and targetsMany:true (returns array); getJsonSchema with includeRelations:true on a belongsTo relation (covers the keyFrom foreign-key branch); model with two indexed properties (covers the indexInfo merge path)
  • repository: BufferType.coerce with explicit encoding options (covers the options ?? {} non-null branch); ObjectType.coerce for already-correct-type instance, primitive, array; ObjectType.serialize with toJSON, without toJSON, and null/undefined
  • rest: RedirectRoute.isRedirectRoute — true, false (no targetLocation), false (wrong description), null, undefined; describe(), updateBindings() no-op, invokeHandler() calls response.redirect; RoutingTable.find throws NotFound without external routes; InfoSpecEnhancer.modifySpec — null pkg skip, override-info path, keep-existing path; parseAuthor with string, object, and null author
  • tsdocs: getUnscopedPackageName with unscoped name (no @ prefix); runExtractorForPackage with silent:false (covers the console.log('> %s', pkg.name) branch)

Only test files are changed; there are no source changes.

Checklist

  • DCO (Developer Certificate of Origin) signed in all commits
  • npm test passes on your machine (ran each affected package's build and tests; lint-staged ran Prettier + ESLint clean on all changed files; full monorepo npm test not run)
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated (n/a, tests only)
  • Documentation in /docs/site was updated (n/a)
  • Affected artifact templates in packages/cli were updated (n/a)
  • Affected example projects in examples/* were updated (n/a)

pranesh-sridhar and others added 7 commits October 22, 2026 13:54
- Interceptor: invoke voter functions declared directly in the
  @authorize metadata, for both allow and deny decisions
- @authorize: throws when applied to a property

Statement, function and line coverage of the package is now 100%.

Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
…configure

Cover the guard for applications without mountExpressRouter, option
merging with defaults, and appPath resolution.

Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
…ixin bindings

- ModelBooter: default and custom options, binding of model classes
- ModelApiBooter: getApiBuilderForPattern lookup and error messages
- BaseArtifactBooter: artifactName, empty dirs/extensions, discover with
  and without debug logging
- Bootstrapper: reuse booter instances when a context is passed to boot()
- BootMixin: PROJECT_ROOT and BOOT_OPTIONS dynamic bindings

Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
…tion and interceptors

Add unit tests for code paths that were not covered by the existing suites:

- Binding: refresh without cache, unconfigured bindings, server scope
  resolution failures, once()
- ContextView: singleValue(), open() reuse, once()
- Context: findByTag with unknown or intersecting tags, observer
  subscribe/unsubscribe/close edge cases
- Injection: hasInjections, inspectInjections, @inject.setter and
  @inject.binding rejecting binding filters, @config.getter/@config.view
  without a current binding
- Interceptors: stacked @intercept on classes and methods, registering
  provider classes and grouped global interceptors, interception proxy
- InvocationContext.toString(), invokeMethod with skipInterceptors,
  BindingKey.validate, isBindingAddress, uuid

Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
…d invoke provider

- ExpressServer: basePath normalization, Express settings, listening and
  url lifecycle, serving under basePath, parent context
- toMiddleware and invokeExpressMiddleware: single/chained handlers and the
  empty-handler error
- getMiddlewareContext: missing request and bound context
- MiddlewareMixin: non-Context super class and empty handler list
- MiddlewareView: group ordering, refresh, validate callback, debug logging
- InvokeMiddlewareProvider: handler arrays, default and custom chains
- sortListOfGroups: debug logging and short group lists

Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
Add unit tests to cover code paths in `@loopback/core` and
`@loopback/metadata` that the existing suites did not exercise.
Coverage was measured with `nyc` over each package's `dist/` output.
Only test files are changed; there are no source changes.

`@loopback/authentication` is covered in loopbackio#11853.
`@loopback/authorization`, `boot`, `booter-lb3app`, `context`,
and `express` are covered in loopbackio#11854.

| Package | Tests before | Tests after | Stmts before | Stmts after |
|---|---|---|---|---|
| `core` | 346 | 355 | 95.47% | 98.74% |
| `metadata` | 162 | 171 | 96.18% | 98.09% |

### Tests added

**`@loopback/core`**

- `application-lifecycle.unit.ts`: `assertInStates()` — throws when the
  application is not in the expected state; does not throw when it is
- `application.unit.ts`: `setMetadata()` — stores metadata under
  `CoreBindings.APPLICATION_METADATA`; overwrites on a second call
- `component.unit.ts`: `mountComponent()` self-reference guard —
  skips a component instance placed directly in its own `components` array
- `lifecycle-registry.unit.ts`: `getObserverGroupsByOrder()` — debug
  logging branch exercised by enabling the `loopback:core:lifecycle`
  namespace temporarily
- `service.unit.ts`: `@service()` without design-time type metadata —
  throws "No design-time type metadata found" when `design:type` is
  absent; `createServiceBinding()` with a `DynamicValueProvider` class —
  trims the `Provider` suffix from the name; preserves a custom name

**`@loopback/metadata`**

- `decorator-factory.unit.ts`: Base-class abstract-method guards —
  `mergeWithInherited()`, `mergeWithOwn()`, and `create()` each throw
  "not implemented" when the subclass does not override them; `getTarget()`
  returns `undefined` for non-object specs; `MethodMultiDecoratorFactory`
  `mergeWithOwn` exercised on a standalone class with no base
- `inspector.unit.ts`: `MetadataInspector.defineMetadata()` — stores and
  overwrites class-level metadata; `getMethodMetadata()` with `methodName`
  omitted defaults to the constructor key

### Verification

All tests pass (`npm test` per package, `lb-tsc` build + `lb-mocha`).
ESLint (`--report-unused-disable-directives`) and Prettier clean on all
changed files.

Signed-off-by: Pranesh Sridhar <Pranesh.Sridhar@ibm.com>
Packages covered and coverage improvements:

| Package | Stmts before | Stmts after | Branches before | Branches after |
|---|---|---|---|---|
| model-api-builder | 0% (no tests) | 100% | 0% | 100% |
| http-caching-proxy | 93.84% | 98.46% | 55.55% | 94.44% |
| openapi-spec-builder | 88.60% | 100% | 72.13% | 100% |
| rest-crud | 68.23% | 100% | 50% | 83.33% |
| filter | 96.83% | 99.36% | 87.09% | 96.77% |
| repository-json-schema | 95.48% | 98.87% | 92% | 94.66% |
| repository (types) | object 68%, buffer 95% br | 100% | 75%/95% | 100% |
| rest | redirect-route 71% | 100% stmts/lines | various | improved |
| tsdocs | 86.39% | 88.43% | 57.62% | 62.71% |

Tests added:
- model-api-builder: asModelApiBuilder binding template, MODEL_API_BUILDER_PLUGINS constant
- http-caching-proxy: constructor, stop() no-op, logError branches (true/false,
  error.stack vs error), non-ENOENT cache corruption integration test
- openapi-spec-builder: withOperationReturningString, withRequestBody,
  withStringResponse default, OperationSpecBuilder/ComponentsSpecBuilder
  accumulation (??= branches), setupOperationId idempotency
- rest-crud: CrudRestComponent binding, CrudRestApiBuilder.build() — missing
  basePath, non-Entity model, repo absent/present, readonly flag
- filter: WhereBuilder.impose and FilterBuilder.impose with falsy internal
  state (defensive branch coverage)
- repository-json-schema: getNavigationalPropertyForRelation targetsMany=false
  and =true, getJsonSchema with belongsTo keyFrom, multiple indexed properties
- repository: BufferType.coerce with explicit options, ObjectType.coerce
  already-instance and invalid values, ObjectType.serialize toJSON/no-toJSON
- rest: RedirectRoute.isRedirectRoute, describe, updateBindings, invokeHandler;
  RoutingTable.find throws NotFound; InfoSpecEnhancer.modifySpec with null pkg,
  overrideInfo flag, string/object/null author parsing
- tsdocs: getUnscopedPackageName unscoped name, invokeExtractorForPackage
  silent:false logging branch

Only test files are changed (plus model-api-builder package.json/tsconfig.json
to wire up the test script and devDependencies, as no tests existed before).

Signed-off-by: Pranesh Ravi <pranesh.ravi@ibm.com>

This branch has not been deployed

No deployments
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