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.yamlthat hasDATABASE_URLset to a PostgreSQL connection string, e.g.postgres://foo:bar@server/app(see below for an example). - Start application using
podman-compose up -dor similar. Folder.keystone/adminis 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/testsis 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 };
}