v0.2.7
Add the logger service accessor, the last binding without one
'logger' was the one container binding with no services/* module.
HttpKernel resolved it for ctx.logger, so a request had one; a
provider, a scheduled task or a console command had to reach for
container.resolve('logger') — which is async — to log a line.
The Ignitor fills the locator at the end of the boot phase, not with the
others: a PROVIDER binds 'logger', so it does not exist when the app,
router and server locators are set. Absent stays a normal state — ream
declares the contract and implements no logger — so the message names
the package that does (@c9up/spectrum) and points at ctx.logger for
request-scoped logging, rather than blaming the boot order.
Worth recording since the parity audit got it wrong: the logger is not
missing from this universe, it is DELEGATED, the way hashing is sigil's
and validation is rune's. And spectrum's pino-shaped overloads already
accept the message-first form ContextLogger declares — #resolveArgs
treats a lone trailing object as merged data when the template carries
no printf token — so the two contracts agree at runtime, not just at the
type level.
Collapse the guard the cast removal left wrapped
Dropping the assertion from isPromise made the condition short enough
to fit on one line, and the formatter says so. Folded into 0.2.7, which
is tagged but not published.
Narrow instead of asserting, in the type guards
Six checks read typeof (x as SomeShape).prop === 'function' — a cast
whose only job was to reach a property, in a guard that had already
proven what it needed with in. TypeScript narrows on in, so the
assertion said nothing the compiler did not already know, and where the
guard had NOT proven it, the cast was hiding that.
Scheduler's two were the case where it mattered: the condition only
checked the value was neither null nor undefined, so the assertion stood
in for a check nobody had made. It now tests object-or-function first —
a thenable function is legal, and the assertion accepted one, so
narrowing to objects alone would have quietly changed what the scheduler
awaits.
Add testUtils.httpServer(), the shape AdonisJS starts a test server with
AdonisJS does not start the server from the client plugin. It exposes
testUtils.httpServer().start(), which returns the function that closes
it, and the call lives in a SUITE hook:
configureSuite(suite) {
if (['functional', 'e2e'].includes(suite.name)) {
return suite.setup(() => testUtils.httpServer().start())
}
}
A suite that does not declare the hook never starts one, so a unit suite
pays nothing. That is the same saving a lazily-booted client buys,
obtained by scoping rather than by deferring — and obtained per suite
rather than per file.
start() returning its own teardown is what makes it a one-liner: a
setup hook may return its undo, so there is no matching teardown to
write and none to forget.
One named difference from AdonisJS: TestUtils is constructed with how
to boot the app, because ream has no ambient application to reach for —
there, testUtils is a container service that already knows its app.
Here the app is whatever the test bootstrap hands over.
Boot the test client on its first request, not on construction
A runner installs the client for every test file, and the server started
whether or not the file ever issued a request. Measured on a real
project: ~400ms per file, on 76 unit files out of 101, which is +31s on
a campaign that runs in 50 — most of its time spent on a server nobody
talks to. One process per file means eager booting cannot be amortised
across the run; there is nothing to share.
boot() is now memoised and optional: the first request awaits it, and
concurrent callers share one. close() is a no-op when nothing started,
so a teardown stays unconditional, and it awaits an in-flight boot
before closing — otherwise a close racing a boot finds no server, and
the one the boot is about to assign keeps running with nobody holding
it.
There is a second cost to booting early. A file with no server has
nothing holding the event loop open, so it needs no forceExit — and
forceExit is global, so switching it on for the few files that do
would silence "a test left something running" for all of them.
port still reads 0 until a server is running, and now returns to 0
after close(): a released port is not a remembered one.
Changes since v0.2.6.