kit icon indicating copy to clipboard operation
kit copied to clipboard

Unable to perform Vitest integrations tests using $env/dynamic/private

Open kyllerss opened this issue 2 years ago • 9 comments

Describe the bug

I finally got around to migrating away from import.meta.env.* for all of my environment variables and onto the SvelteKit-recommended $end/dynamic|static/public|private. I had a number of integration tests I had written in vitest that are now breaking with the following error:

FAIL  tests/integration/hooks.test.ts [ tests/integration/hooks.test.ts ]
Error: Cannot import $env/dynamic/private into client-side code
 ❯ Context.load node_modules/@sveltejs/kit/src/exports/vite/index.js:304:12
 ❯ Object.load node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:40201:46
 ❯ loadAndTransform node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:36553:24

My integration tests import a number of back-end modules (eg. database access code, etc...).

I am surprised by the fact that the integration tests are being treated as client code that is tripping up the server-client serialization safety mechanism.

I am at a loss as to what to do here. Any recommendations on how to address this issue? Is there any way I can turn off this safety mechanism for my integration tests? Is there something entirely different I should be doing to do database integration tests?

PS. My reproduction project is using the stock configuration that comes from a new project. It calls the vitest tests unit tests. I am not sure if I should be reading too much into that. If vitest is not the right place for writing these types of tests, what alternatives should I look into?

Reproduction

The reproduction test illustrates the problem when importing a class that makes a reference to $env/dynamic/private.

https://github.com/kyllerss/sveltekit-issue-8180

Logs

No response

System Info

System:
    OS: Linux 5.15 KDE neon 5.26 5.26
    CPU: (16) x64 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
    Memory: 9.68 GB / 15.34 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 16.18.0 - ~/apps/node-v16.18.0-linux-x64/bin/node
    npm: 8.19.2 - ~/apps/node-v16.18.0-linux-x64/bin/npm
  Browsers:
    Brave Browser: 108.1.46.144
    Chrome: 108.0.5359.124
    Firefox: 108.0
  npmPackages:
    @sveltejs/adapter-auto: ^1.0.0 => 1.0.0 
    @sveltejs/kit: ^1.0.0 => 1.0.0 
    svelte: ^3.54.0 => 3.55.0 
    vite: ^4.0.0 => 4.0.1

Severity

serious, but I can work around it

Additional Information

No response

kyllerss avatar Dec 15 '22 14:12 kyllerss

Mhhhm, anything that ends with .server.js/ts is treated as "this code is safe". I guess we need to make the detection algorithm more sophisticated to also work with test files.

dummdidumm avatar Dec 15 '22 15:12 dummdidumm

Can you describe a realworld usecase for this? The example repository has a env-dynamic-private.test.ts test which directly imports environment variables, but that doesn't seem too realistic to me. If you have a +server.js file then I'd expect your test to be named something like server.test.js, which should work. Obviously that's not what you're doing, but it's not clear to me what the actual usecase is here

benmccann avatar Dec 17 '22 05:12 benmccann

Ah, I can see my test example was confusing. I have updated the issue reproduction to be a little closer to what I am trying to do.

The use-case is using server-side code ($lib/server) directly in the tests for the purposes of seeding the test database and verifying db results. Code that accesses the database connection string (among other server-side properties) is not accessible/importable.

I tried changing the tests to follow the naming conversions you mentioned, but that didn't work either.

If there are naming conventions I should be using instead or something else I should be doing, I'd be happy to adopt them.

kyllerss avatar Dec 18 '22 14:12 kyllerss

Hmm, yeah, I see your test is also in $lib/server, so I would expect that to work: https://github.com/kyllerss/sveltekit-issue-8180/blob/main/src/lib/server/db_service.test.ts

@tcc-sejohnson any ideas about this one?

benmccann avatar Dec 19 '22 17:12 benmccann

Setting an alarm to take a look at this tonight.

For me, my unit test files (ending in .spec.ts) sit next to the actual files they are testing (e.g. in $lib/stores); when I import env from $env/dynamic/private, it's detected as usage in client-side code.

DoisKoh avatar Dec 21 '22 01:12 DoisKoh

@DoisKoh are you importing $env/dynamic/private in your test or in your actual code? Can you share the file names of your code and corresponding test?

benmccann avatar Dec 21 '22 03:12 benmccann

I'm importing it in my tests (not actual code).

File names/paths: Actual Code: src\lib\components\stores\Clients.ts Test Code File: src\lib\components\stores\Clients.spec.ts

DoisKoh avatar Dec 21 '22 05:12 DoisKoh

This is the problem:

https://github.com/sveltejs/kit/blob/4b7fc72fc57db3c893b1240611611c36eb1f95a6/packages/kit/src/exports/vite/index.js#L303-L337

During dev, normally, options.ssr is true when a module is loaded server-side and false when it's not -- and the only way a module could be loaded "not server-side" is when accessing a page module via a request. During testing, options.ssr is false, but obviously in most cases we're not loading from a routes folder... I'm not really sure what the best way to fix this is -- we don't have access to the import chain, so we can't check the parents for {test|spec} files. We might be able to say "if these test environment variables are set, don't throw", but I think we'd probably still want this behavior in integration tests and even some unit tests, so I don't love that solution.

@Rich-Harris The only immediate solution I can think of is to undo your work here and go back to the more-complicated solution we had before that allowed us access to the module graph during dev. That seems like throwing the baby out with the bathwater to me, though... hopefully I'm missing something obvious.

I can confirm I am also having this issue in my .test.ts files running through vitest.

Funny thing is that the file im testing (user.ts - that exports a bunch of functions that do simple CRUD operations related to users in db) actually imports my prisma instance from $lib/server/db.ts and it works just fine. It's only when I import a file with .svelte extension it actually throws.

Luckily in my case there is a workaround - I can move the component files out of the server directory.

When it comes to importing from $env/static/private I replaced it in my setupTests.ts file with:

vi.mock('$env/static/private', () => import.meta.env);

Hope that helps

bartektelec avatar Dec 31 '22 13:12 bartektelec

I actually don't believe we need to overthink this — we can just do this: https://github.com/sveltejs/kit/pull/8365

Rich-Harris avatar Jan 06 '23 15:01 Rich-Harris

I can confirm I am also having this issue in my .test.ts files running through vitest.

Funny thing is that the file im testing (user.ts - that exports a bunch of functions that do simple CRUD operations related to users in db) actually imports my prisma instance from $lib/server/db.ts and it works just fine. It's only when I import a file with .svelte extension it actually throws.

Luckily in my case there is a workaround - I can move the component files out of the server directory.

When it comes to importing from $env/static/private I replaced it in my setupTests.ts file with:

vi.mock('$env/static/private', () => import.meta.env);

Hope that helps

@bartektelec lead me in the right direction. Posting this for others. We ended up using the described methods with setupTests.ts, however we intercepted the preferred SvelteKit environment variables.

import { vi } from 'vitest';

vi.mock('$env/dynamic/public', () => {
  return {
    env: {
      PUBLIC_NODE_ENV: 'test',
    }
  };
});

vi.mock('$env/dynamic/private', () => {
  return {
    env: {
      SUPER_SECRET_KEY: 'test',
    }
  };
});

csellis avatar Dec 07 '23 11:12 csellis

for anyone who landed here struggling with running generic typescript scripts locally:

TEST=true pnpx vite-node src/scripts/foo.ts

this did the trick for me. Setting TEST=true disables the Cannot import $env/dynamic/private into client-side code error locally.

tymonTe avatar Feb 16 '24 09:02 tymonTe

Is anyone still running into this issue? I am still seeing it even when I run TEST=true npm run test and I tried adding the stubs to setupTests.ts.

Node: v18.20.1
├── @sveltejs/[email protected]
├── @sveltejs/[email protected]
├── @sveltejs/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]

Error:

 FAIL  src/lib/migrate.test.ts [ src/lib/migrate.test.ts ]
Error: Failed to resolve import "$env/dynamic/public" from "src/lib/migrate.ts". Does the file exist?

craigkai avatar May 28 '24 15:05 craigkai

Ignore me, I had svelte (not kit) plugin enabled in my svelte config which was the issue.

craigkai avatar May 28 '24 15:05 craigkai