fakesnow
Intro
Depot Local can serve a Snowflake location from
fakesnow — a local Snowflake emulator built on DuckDB and
SQLGlot — instead of a Snowflake account. Depot Local starts the container, creates the databases,
and writes the connection details the connector reads, so a suite that exercises the Snowflake path
needs no account, no private key, no AWS profile and no depot.properties of your own.
The location itself is an ordinary SNOWFLAKE location. Nothing about your schema, your environment
or your requests changes — only where the connection points. A package that already has Depot Local
Snowflake tests needs one config field and one line at the location.
This is the Depot Local flavour. For a Depot Test suite — DepotTest.in(namespace)… with
setContent and check steps — use the FAKESNOW location type
instead, where the Depot Test backend owns the container. The two are separate surfaces; this page is
only about DepotLocal.
The caution on the Snowflake page still applies: a Depot Local Snowflake test is slower than the equivalent Depot Test transaction test, so reach for it when a transaction test cannot express what you need — queries, the lambda or GQL API, the gateway interface.
Example
Add fakesnow to the config, and every Snowflake location in the run points at the container:
const depot = new DepotLocal({
depot: {},
// dynamodb is required for snowflake tests
dynamodb: {},
fakesnow: {} // starts the fakesnow container and points Snowflake locations at it
});
beforeAll(async () => {
await depot.start();
});
afterAll(async () => {
await depot.stop();
});
Build the environment with a fakesnow-served location:
const namespace = Schemas.fromYamlFiles("test/schema/pet.yml");
const environment = new EnvironmentBuilder(namespace)
.location(LocationBuilder.fakesnowDb())
.build();
const api = depot.deploy(environment).api(0);
const create = await api.create({
schema: "pet.Pet",
data: {name: "Bandit", age: 3}
});
LocationBuilder.fakesnowDb() names the intent, so a test says what it runs against; it returns the
same SNOWFLAKE location snowflakeDb() does. Use LocationBuilder.fakesnow(id) when you need to
choose the location id yourself.
deploy() only registers the environment — the connector deploys the schema on the first
request, so the DDL runs against fakesnow when your first api call arrives, not at deploy().
For a working example, see
packages/depot-local/test/fakesnow.test.ts.
What Depot Local does for you
Worth knowing, because each of these is a thing you would otherwise have to do by hand:
- Starts the container on the run's Docker network. The connector reaches it by network alias
rather than
localhost— the two are in different containers — while your test process reaches it on a host port Docker assigns, so concurrent runs do not collide. - Creates the databases. Snowflake refuses
USE DATABASEfor a database that does not exist and so does fakesnow; on a real account someone created it once, by hand. Depot Local createsDEPOT_TESTbefore the connector starts. - Writes the
depot.propertiesthe connector reads, into the directory it mounts at/env:sql.endpointOverrideplus fakesnow's fixed credentials. The connector then stamps those onto every Snowflake executor in the environment. Your owndepot.properties, if you have one, is not involved and not read.
The credentials are fixed and carry no secret: account fakesnow, user fake, password snow,
database DEPOT_TEST, warehouse DEPOT_TEST_WH. Password auth, so no key pair anywhere.
fakesnow and a real Snowflake are mutually exclusive for a run. Depot stamps one set of
Snowflake credentials over every Snowflake executor in the environment, so an environment cannot
have one location on fakesnow and another on an account.
What works, and what does not
It is the same emulator the FAKESNOW location type uses, so the parity
boundaries are the same ones that page lists — no S3 behind a stage (so no export), no stored
procedures, and Snowflake's proprietary HASH values differ, since fakesnow computes DuckDB's hash.
A test that asserts a recorded Snowflake hash belongs on a real account.
One Depot check does not run: Depot normally compares each view's stored DDL against the text it submitted, and only Snowflake echoes that text verbatim. Pointing at any endpoint override skips that comparison. Data checks are unaffected.
Options
| Variable | Effect |
|---|---|
DEPOT_FAKESNOW_IMAGE | Run a fakesnow image of your own instead of the pinned one, e.g. fakesnow:local. The same variable the Java side reads, deliberately. |
DEPOT_FAKESNOW_MEMORY | The container's memory limit, such as 2g or 1536m: whole bytes, or a whole number with k, m or g (binary). Unset, no limit. The same variable the Java side reads. |
DEPOT_FAKESNOW_DEBUG | Passed to the container as FAKESNOW_DEBUG for fakesnow's own SQL tracing: snowflake to see each statement as Depot sent it, anything else to see the DuckDB SQL it was translated into. |
DEPOT_LOCAL_VERSION | Overrides the connector image version. See the troubleshooting note below. |
With a memory limit, DuckDB gets about 70% of it and spills to disk past that. A container that
needs more than its limit is killed, and the tests then fail on a lost connection rather than an
out-of-memory error, so raise the limit if that happens. The limit applies to a container the run
starts: one reused through DEPOT_LOCAL_KEEP_ALIVE keeps the limit it started with. The Java side
picks a budget of its own when the variable is unset.
fakesnow: { showLogs: true } brings the container's output into your test log. port,
strictHostPort and containerPort behave as they do for the other services, though there is rarely
a reason to pin the port — nothing outside the run connects to it.
Troubleshooting
No credentials defined for dataset … default executor depot-local-snowflake-executor
The connector image is older than the fakesnow support: sql.endpointOverride means nothing to it,
so it discards the credentials Depot Local wrote and the executor reaches the request with none.
The image version comes from @stage-tech/depot-config's platformVersion — but from the copy in
your workspace's node_modules, which a yarn install can leave older than
packages/depot-config. Nothing keeps the two in step, so a run can silently use an image from
weeks before the pin. Check which one you have, and until it is refreshed, override it:
DEPOT_LOCAL_VERSION=<the version packages/depot-config pins> yarn vitest run test/fakesnow.test.ts
Suspect this before suspecting the wiring — the symptom appears several layers from the cause, and
the wiring is pinned by tests on the platform side (FakesnowCredentialsTest).
A create returns 409 for a row that is not there
A write reports its row count through the JDBC driver, and Depot reads anything but 1 as "this
object already exists". Fixed in the fakesnow image; if it reappears, you are on an older image than
the pinned one — check DEPOT_FAKESNOW_IMAGE.
Catalog Error: … does not exist for a database other than DEPOT_TEST
Depot Local creates DEPOT_TEST only. A schema that names a different database needs it created
first — fakesnow, like Snowflake, will not conjure one.