feat(appkit): add testing-kit helpers for env, errors, context, and cache - #555
Open
IamGalymzhan wants to merge 19 commits into
Open
feat(appkit): add testing-kit helpers for env, errors, context, and cache#555IamGalymzhan wants to merge 19 commits into
IamGalymzhan wants to merge 19 commits into
Conversation
withEnv(vars, fn) sets env for the duration of fn and restores each key's prior state on exit — including deleting keys that were previously unset, rather than the blanket delete the current test pattern uses. Sync and async forms; nested calls restore LIFO. Exported from @databricks/appkit/testing. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
createApiError({ statusCode, message, errorCode }) returns a genuine ApiError
instance, so error-path tests can assert real instanceof ApiError checks
instead of hand-rolled look-alikes that pass name but fail instanceof.
Exported from @databricks/appkit/testing.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
An optional second parameter { responses, env, strict } composes a plugin
unit-test in one call: a mock workspace client seeded from responses, an
installed service context wired to it, and scoped env — all auto-restored via
an afterEach hook, with an idempotent restore() escape hatch. Synchronous and
non-breaking: no-options callers are unchanged. Exports TestPluginContextOptions.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Replaces 11 hand-rolled process.env set/try/finally/delete blocks with withEnv across the files, jobs, and ai-search test suites. Behavior-preserving; the vi.mock/vi.hoisted preludes are untouched (the broader internal-mock migration is deferred). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Documents withEnv, createApiError, and the createTestPluginContext options parameter in the testing guide, and surfaces the already-shipped createMockRouter fixture. The composition example is shown synchronously (no await on the factory). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…tests Removes the unused ServiceContextMock import and three side-effect-only mock bindings, and stops capturing an unused priorInitialized — clearing the lint warnings the composition-options work introduced. No behavior change. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…aths Addresses two code-review findings. Extracts a shared applyEnv() helper so withEnv and the createTestPluginContext options path no longer duplicate the env capture/restore loop. Guards withEnv's error paths so a failing restore can no longer mask the caller's original error (a restore failure still surfaces on the success path). Behavior-preserving for normal use; all tests pass. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
This was referenced Aug 27, 2026
Boots AppKit's real in-memory cache singleton and clears it before each test, exposing the real CacheManager so plugin tests assert cache-key behaviour through production's generateKey instead of mocking the internal cache module. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Shared, unexported factory for the passthrough cache instance the non-behavioural suites mock; adopted across those suites in a follow-up unit. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Drops the hand-rolled functional cache fake; the suite now runs against the real in-memory cache. Reworks the abort-fallback test to model a real client disconnect (response close aborts the handler signal) rather than spying the signal executeStatement happens to receive. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Runs against the real in-memory cache; the cache-key-equality test now spies the real getOrExecute to capture the composed key parts instead of a fake. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Runs against the real in-memory cache. Completes the telemetry mock span (adds end/addEvent/etc.) since the real getOrExecute opens a span the old functional cache fake never did. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Replaces the copy-pasted passthrough cache fake with createCacheMock(), pulled in via an async vi.hoisted + dynamic import (require can't resolve the TS helper inside a hoisted block). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…uites Replaces the copy-pasted passthrough cache fake in 16 suites (files/*, genie, jobs, serving, lakebase, connectors/lakebase, analytics.readonly) with createCacheMock(). server.test.ts keeps its own mock (close(), not a passthrough getOrExecute). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
The fake omitted the third (userKey) argument the real getOrExecute takes, so calling it with three args failed typecheck. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
The kit shipped two hook-wiring helpers — useServiceContextMock and, in this
stack, useTestCache — and none for the app itself, so a suite needing an app
per test hand-wired beforeEach/afterEach and had to remember close(). That gap
widened when useTestCache adopted the pattern for the cache and skipped the
app.
It matters more since the harness began allowing one open app at a time. A
forgotten close() used to leak a listener quietly; now the next boot throws and
takes the rest of the suite with it. await using is still shorter for a single
test, but it cannot carry an app from a beforeEach into the test body, and a
describe holding one in beforeAll cannot contain a test that boots its own — so
beforeEach/afterEach is the remaining pattern for per-test apps, and it was the
one without a helper.
Mirrors useTestCache: same { current } accessor, same call-it-in-a-describe
rule, same self-explaining error when read outside a test. The afterEach clears
the handle before awaiting close, so a close that throws cannot leave a stale
app readable by the next test.
The one-app-at-a-time guard makes the tests discriminating: verified by
removing the close, which fails four of them with "a harness app is already
open" rather than passing quietly. Removing the barrel export fails the
published-surface assertion by name.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…ocks Fourteen suites hand-rolled a workspace client and an ApiError look-alike, then patched `../../workspace-client` and `../../context` so production code would pick them up. None of that expressed anything the shipped kit cannot: the client is `createMockWorkspaceClient`, the error is `createApiError`, and injecting the client through `mockServiceContext` makes `getWorkspaceClient()` resolve to it via the real ServiceContext — so nineteen of twenty-four module mocks go away. `setupTestEnv` in the files suite now takes the client and passes it through, which is what let nine files drop their `context` mock at once. Strictness is preserved deliberately, not incidentally. A hand-rolled object literal threw a TypeError on any undeclared call; `strict: true` keeps that loudness with a sentence instead of a stack trace. Verified by counting rather than asserting: 306 tests before and after, 558 expects before and 559 after. One class of assertion gets stronger. `connectors/files` asserted `toBeInstanceOf(MockApiError)` — circular, since it could only pass while the module was patched to install that fake. It now asserts the real `ApiError`, and a mutation that throws a plain Error fails it, which the old form could not catch. Two mocks are kept on purpose. jobs keeps a three-line `Context: vi.fn()` — the SDK cancellation-token class, an unrelated concern the old mock also served. The nine files that mock `workspace-client` to stop a real client being built have no injection seam (they test ServiceContext, CacheManager, or the type-generator), so module mocking is correct there. Not migrated: files/plugin.test.ts (3978 lines, 49 client refs, 7 local getRouteHandler copies) wants a reviewed diff rather than a scripted one, and ai-search pins getCurrentUserId constant on purpose so cache-key scoping is driven only by executorKey — the real path would change the keys its tests assert on. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
Each PR targets the one above it, so the diff here is only the delta on top of #540. Review in order.
Summary
Adds customer-facing helpers to
@databricks/appkit/testingso plugin tests assert business logic instead of hand-rolled boilerplate, then adopts them across the existing suites.New helpers
withEnv(vars, fn)— set env for the duration offn, then restore each key's prior state (deleting keys that were unset before). Sync and async forms; nested calls restore LIFO. Replaces theprocess.env.X = …; try/finally; deletepattern, which wrongly drops pre-existing env.createTestPluginContext(fakes, { responses, env, strict })— a synchronous options parameter that composes a plugin unit-test in one call: a mock workspace client fromresponses, an auto-restored service context wired to it, and scopedenv. Non-breaking — calls with no second argument are unchanged.createApiError({ statusCode, message, errorCode })— a genuineApiError(soerror instanceof ApiErrorholds) for error-path tests.useTestCache()— boots AppKit's real in-memory cache, clears it before each test, and returns the realCacheManager, so a plugin's caching path runs through production's owngetOrExecute/generateKeyinstead of a mockedcachemodule.CacheManagerstays a process singleton.Adoption
vi.mock("cache")fakes and assert against the real cache viauseTestCache().createCacheMock()in place of a copy-pasted fake.withEnvreplaces 11 hand-rolled env blocks, andcreateMockRouteris surfaced in the testing guide.The internal-mock migration and the singleton-reset changes stay deferred to a later PR.
Stacked on #540 (
feat/testing-kit-harness) — these helpers extend testing-kit code on that branch, so this targets it rather thanmain.