Skip to content

feat(unit): expose real implementations for dependency tokens - #1121

Merged
omermorad merged 5 commits into
masterfrom
feat/v4-token-exposure
Nov 9, 2026
Merged

omermorad merged 5 commits into
masterfrom
feat/v4-token-exposure

Conversation

@omermorad

@omermorad omermorad commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Sociable tests cannot currently construct real implementations behind string, symbol, or abstract-class dependency tokens. Add an explicit binding while retaining existing Unit v4 behavior:

const { unit, unitRef } = await TestBed.sociable(CheckoutService)
  .expose(PRICING, PricingService)
  .mock(ExchangeRates)
  .impl((stub) => ({ rate: stub().mockReturnValue(1.2) }))
  .compile();

Fixes #464. This is an additive Unit v4 feature.

Craig's original contribution

This continues @craig0990's proposal and implementation in #465. Commit 477ae5c4 ports his original 211ed87d181e4bba1ca00bd29371663648820c1f and preserves Craig Roberts's Git authorship and original author date, with the source commit recorded in the message. The author email uses Craig's public GitHub-linked identity, as verified on his earlier merged contribution #529, so GitHub attributes the commit to @craig0990. The port resolves conflicts against the current architecture and retains the existing array-based core contracts. Follow-up validation, abstract-token support, documentation, and packed-consumer coverage are separate commits.

Behavior

  • .expose(Class) retains its existing behavior. .expose(token, Implementation) constructs the supplied implementation for a string, symbol, abstract-class, or concrete-class token.
  • Class-token implementations are checked against the token's instance type. Plain strings and symbols do not independently encode an interface type.
  • Repeated requests for the same token and metadata share an instance. Different tokens or metadata qualifiers remain independent, including when their implementation class is the same. A token binding does not implicitly expose or alias its implementation's class token.
  • A binding applies across that token's metadata qualifiers. Qualified .mock(...).auto(), .impl(), and .final() declarations can replace individual occurrences and take precedence over exposure.
  • Unexposed dependencies retain ordinary v4 automatic mocking. Exposed instances are accessed through the unit; unitRef.get() remains for mocks.
  • Reject conflicting implementation declarations, report token constructor cycles, and warn about unreachable bindings.
  • Keep token exposure in selective sociable mode. Combining it with .compose() remains unsupported. This PR does not add fail-fast behavior or change defaults.

The test supplies the binding explicitly. Suites does not inspect or verify the production container's registration, so tests must keep their chosen implementation aligned with production wiring when that is relevant.

Compatibility and release

Existing core array parameters, custom construction strategies, and DI/doubles adapter contracts are retained. Bindings use an optional resolver setting; new resolution results and constructor parameters are additive. No adapter package changes or manual version/changelog edits are included.

The repository's actual conventional-commit release preset recommends minor bumps for @suites/core.unit and @suites/unit from this PR, with no breaking-change notes. Preserve the authored conventional commits when merging.

Validation

  • Frozen-lockfile install, full build, lint, and package tests passed locally on Node 26.13.0.
  • Final core run: 105 tests passed, including the existing integration suites and new binding regressions.
  • All ten packed consumers passed runtime suites and both normal/skipLibCheck typecheck variants across Jest/Vitest, NestJS/Inversify/InjectionJS, and supported CJS/ESM combinations.
  • New consumer coverage verifies string/symbol/abstract bindings, nested construction, identity, qualified overrides, property injection where supported, normal automatic mocks, and negative type assertions for incompatible or abstract implementations and lost subject inference.
  • Independent runtime and typing reviews completed. The typing review identified a compiler-floor regression, now corrected and covered by a TypeScript 5.2.2 fixture with skipLibCheck: false. The reviewer verified that the fixture fails against the previous declarations and passes with the correction.
  • Hosted Node 22/24/26 validation for the updated head is pending. This PR is a draft for review; nothing has been merged or released.

craig0990 and others added 4 commits November 9, 2026 11:06
Port Craig Roberts's string/symbol exposure mapping from #465 to the current builder and resolver. Preserve the existing array-based core contracts through optional binding configuration, and cache real instances by the requested token.

(cherry picked from commit 211ed87)
Keep identity per token and metadata, allow explicit mock overrides, and diagnose cycles, conflicting implementations, and unreachable bindings. Preserve existing core constructor parameters and adapter interfaces.
Add expose(token, Implementation) for string, symbol, and typed class tokens. Preserve v4 implicit mocking and existing expose(Class) calls. Document identity, override precedence, construction boundaries, and the distinction from production container wiring.
@omermorad omermorad added enhancement New feature or request target: v4 Compatible fixes and opt-in additions for Suites Unit 4 area: core Dependency resolution, caching, and test bed behavior area: api Public TestBed API and configuration labels Nov 9, 2026
@codecov

codecov Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.87234% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.26%. Comparing base (074388f) to head (4b49d2a).

Files with missing lines Patch % Lines
...ore/src/services/builders/sociable-unit-builder.ts 94.44% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1121      +/-   ##
==========================================
+ Coverage   92.84%   93.26%   +0.41%     
==========================================
  Files          27       29       +2     
  Lines         615      668      +53     
  Branches      105      118      +13     
==========================================
+ Hits          571      623      +52     
  Misses         27       27              
- Partials       17       18       +1     
Flag Coverage Δ
core.unit 96.85% <97.87%> (-0.02%) ⬇️
di.injectionjs 100.00% <ø> (?)
di.inversify 87.73% <ø> (ø)
di.nestjs 87.25% <ø> (ø)
doubles.jest 100.00% <ø> (ø)
doubles.vitest 97.05% <ø> (ø)
unit 84.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Infer implementation types from the token constructor without requiring the NoInfer builtin. Verify existing and new exposed dependency consumers with TypeScript 5.2.2 and declaration checking enabled.
@omermorad
omermorad force-pushed the feat/v4-token-exposure branch from 3f7c66b to 4b49d2a Compare November 9, 2026 14:39
@omermorad
omermorad marked this pull request as ready for review November 9, 2026 14:42
@omermorad omermorad self-assigned this Nov 9, 2026
@omermorad
omermorad merged commit fee4da6 into master Nov 9, 2026
48 checks passed
@omermorad
omermorad deleted the feat/v4-token-exposure branch November 9, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: api Public TestBed API and configuration area: core Dependency resolution, caching, and test bed behavior enhancement New feature or request target: v4 Compatible fixes and opt-in additions for Suites Unit 4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to expose sociable dependencies with string / symbol based tokens

2 participants