keystone
keystone copied to clipboard
Application tries to reuse test state during normal runtime (breaks when restarting after running tests)
Please add steps to reproduce the problem
- Use Docker or Podman with a
compose.yaml
that hasDATABASE_URL
set to a PostgreSQL connection string, e.g.postgres://foo:bar@server/app
(see below for an example). - Start application using
podman-compose up -d
or similar. Folder.keystone/admin
is generated, and application starts successfully. - Write a database test using SQLite as provider, and a local file path, e.g. in
/tmp
(see below for an abbreviated example). - Run tests, using e.g.
jest --testTimeout=60000
. The folder.keystone/tests
is generated, and tests pass. - Restart the application using
podman-compose restart app
, Startup fails with the error message below.
PrismaClientInitializationError: error: Error validating datasource `sqlite`: the URL must start with the protocol `file:`.
--> schema.prisma:5
|
4 | datasource sqlite {
5 | url = env("DATABASE_URL")
|
Validation Error Count: 1
at Object.loadEngine (/app/node_modules/.prisma/client/runtime/index.js:35591:19)
at Object.instantiateLibrary (/app/node_modules/.prisma/client/runtime/index.js:35520:5)
at Object.start (/app/node_modules/.prisma/client/runtime/index.js:35670:5)
at Object.connect (/app/node_modules/@keystone-6/core/dist/initConfig-64706830.cjs.dev.js:3138:13)
at setupInitialKeystone (/app/node_modules/@keystone-6/core/scripts/dist/keystone-6-core-scripts.cjs.dev.js:422:3)
at initKeystone (/app/node_modules/@keystone-6/core/scripts/dist/keystone-6-core-scripts.cjs.dev.js:166:35) {
clientVersion: '3.11.0',
errorCode: 'P1012'
}
Please describe what you expected to happen
The application starts up without any issues, and tests also run without any issues.
Please add contextual information such as your node version (node -v), or the web browser you used
The problem seems to be that in step 5 above, the .keystone/admin
folder is removed and regenerated but, before it is regenerated, Keystone seems to select .keystone/tests
for use during actual runtime of the application. When that happens, it starts using the schema.prisma
from .keystone/tests
, which has the SQLite provider set, and then tries to apply the DATABASE_URL
environment variable containing PostgreSQL from compose.yaml
to it. If you remove .keystone/tests
first, the application starts normally again.
Removing schema.prisma
alone is not enough, Keystone still seems to want to descend into .keystone/tests
for some reason.
Test and normal application state appears to not be (properly) separated, due to which they are interfering with each other's data. I also looked if it was necessary to set some sort of environment variable when running tests, but could not find any.
Versions
- Keystone version: 25 March 2022
- Node.JS version: 16 (Alpine)
compose.yaml
:
services:
app:
image: node:16-alpine
working_dir: /app
command: npm run dev
ports:
- "3000:3000"
volumes:
- ./:/app
environment:
DATABASE_PROVIDER: postgresql
DATABASE_URL: postgres://postgres:bar@postgresql/app
postgresql:
image: postgres:13-alpine
ports:
- "5432:5432"
volumes:
- ./.containers/postgresql/:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: bar
Abbreviated Test Example
(Might contain errors, I copied over the code to get you started more quickly.)
import { TestEnv } from "@keystone-6/core/testing";
import { KeystoneContext } from "@keystone-6/core/types";
import { getTenantFilterQuery, UserGroup } from "../../../src/auth";
import {
createTenant,
createTestContextForUser,
createUser,
UserId,
} from "../../fixtures";
describe("Access control", () => {
let testEnv: TestEnv;
let context: KeystoneContext;
beforeEach(async () => {
({ context, testEnv } = await provisionTestDatabase());
});
afterEach(async () => {
await testEnv.disconnect();
});
test(`Foo`, async () => {
// ...
});
});
async function provisionTestDatabase(): Promise<{
context: KeystoneContext;
testEnv: TestEnv;
}> {
let databaseName = await hash(expect.getState().currentTestName);
const path = `/tmp/${databaseName}.sqlite`;
appConfig.db.url = `file:${path}`;
appConfig.db.provider = "sqlite";
let testEnv = await setupTestEnv({ config: appConfig });
let context = testEnv.testArgs.context;
await testEnv.connect();
return { context, testEnv };
}