AVA hangs after all tests pass #3259
-
I have a small project with a simple test suite, with no async code (that I'm aware of, at least!): https://github.com/ursalang/ark When I run the tests, they all pass, but AVA hangs and runs indefinitely at 100% CPU. The output looks like this:
At the end, AVA is still running. AVA configuration (from "ava": {
"typescript": {
"rewritePaths": {
"src/": "lib/"
},
"compile": "tsc"
}
}, AVA invocation: Apologies for not providing a minimal example yet; I will endeavour to whittle it down. I note already that even running one of the tests using e.g.
will cause the hang sometimes. The more tests I run, the more often it hangs, it seems. Running one of the two test files:
will often but not always hang. The code being run is entirely deterministic and non-async, so I can't see any reason why I should get different results on different runs. I tried running the test suite on two different machines "just in case" and got the same symptoms. Thanks for AVA! |
Beta Was this translation helpful? Give feedback.
Replies: 12 comments 11 replies
-
The problem seems to be with worker threads. Works fine if you use the |
Beta Was this translation helpful? Give feedback.
-
Thanks ever so much for the quick response and workaround. Is there anything else you need me to do here? It sounds as though you're saying it looks like a problem with AVA? |
Beta Was this translation helpful? Give feedback.
-
I noted this is happening to me after v6 is released because my code is using puppeteer and it's spawning a process that is still running after all tests are pasing green. The ideal solution for this is to control the process lifecycle and kill it before ava exits. I did that and it worked. However, there are other scenarios where do that is not easy. I was wondering if ava make this easier just waiting to a specific process signal (like SIGTERM) or provide a mecanihsm to do it, something a global teardown: if (NODE_ENV === 'test') require('ava').teardown(browser.close) or maybe as cli flag? thinking into |
Beta Was this translation helpful? Give feedback.
-
I have the same error here. Please help. |
Beta Was this translation helpful? Give feedback.
-
I also have this issue, for me it occurs 100% of times but I do have async stuff happening. I am trying to test some trpc endpoints so my environment is Node with esm. Not sure if this is relevant but here is my tsconfig.json seeing as I use typescript with tsx: {
// "include": ["./", "./**/*.ts"],
"allowSyntheticDefaultImports": true,
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "ES2022",
"moduleResolution": "Bundler",
/* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"rootDir": ".",
"baseUrl": ".",
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
I have logged before and after everything and made sure all the tests finish including teardowns + the final after. I am testing with only 1 file which looks like this for reference: import anyTest from 'ava';
import { TypedTestFn, hardcodedReusableTestUsers } from './utils';
import { createTestContext } from './utils';
import { db } from 'db/drizzle';
import { user } from 'db/schema';
import { eq } from 'drizzle-orm';
const test = anyTest as TypedTestFn
test.before(async t => {
t.context = await createTestContext()
})
test.serial('Create', async t => {
t.timeout(2000)
const newUser = await t.context.callers.admin.client.create({
userData: {
password: 'ABCD',
phone: '666424324321231',
name: 'TESTIMUS MAXIMUS',
email: 'whatever@sdfafdsafewafsda.com'
},
note: 'idk this is optional',
discountData: {
flatValue: 10,
percentage: 1,
}
})
console.log('newUser: ', newUser)
// t.notThrowsAsync(t.context.callers.admin.client.create({
// userData: {
// password: 'ABCD',
// phone: '666424324321231',
// name: 'TESTIMUS MAXIMUS',
// email: 'whatever@sdfafdsafewafsda.com'
// },
// note: 'idk this is optional',
// discountData: {
// flatValue: 10,
// percentage: 1,
// }
// }))
// t.is(1,1)
t.pass('All good I hope')
t.teardown(async () => {
console.log('doin teardown')
await t.context.db.delete(user).where(eq(user.id, newUser.userId))
console.log('teardown done')
})
} );
test.after.always(async () => {
console.log('AFTER start')
await db.delete(user).where(eq(user.phone, hardcodedReusableTestUsers.user.phone))
await db.delete(user).where(eq(user.phone, hardcodedReusableTestUsers.admin.phone))
console.log('AFTER DONE')
}) Output:
This is a small utils file that might be relevant seeing as this is where I declare the function that instantiates my context and declare my types: import { type TestFn } from 'ava';
import { db } from '../db/drizzle';
import { client, user } from 'db/schema';
import { appRouter, t, emptyContextMethods } from '../trpc/index';
import { eq } from 'drizzle-orm';
export const createApiCaller = t.createCallerFactory(appRouter)
export const hardcodedReusableTestUsers = {
user: {
name: 'UserName1321',
phone: '1111',
email: 'user.kekimus1111@test.com',
password: 'whatever'
},
admin: {
name: 'AdminUserName1230',
phone: '0000',
email: 'adminUser.kekimus0000@test.com',
password: 'test_admin_user_password',
isAdmin: true
}
}
export const createTestContext = async (shouldThrowAway: boolean = false) => {
const [testUserFromDb] = await db.insert(user).values({
...hardcodedReusableTestUsers.user
}).returning()
const [testAdminUserFromDb] = await db.insert(user).values({
...hardcodedReusableTestUsers.admin
}).returning()
const [testUserClient] = await db.insert(client).values({
userId: testUserFromDb.id
}).returning()
const [testAdminUserClient] = await db.insert(client).values({
userId: testAdminUserFromDb.id
}).returning()
if(shouldThrowAway) {
await db.delete(user).where(eq(user.id, testUserFromDb.id))
await db.delete(user).where(eq(user.id, testAdminUserFromDb.id))
await db.delete(client).where(eq(client.id, testUserClient.id))
await db.delete(client).where(eq(client.id, testAdminUserClient.id))
}
const testUser = {
...testUserFromDb,
client: testUserClient
}
const testAdminUser = {
...testAdminUserFromDb,
client: testAdminUserClient
}
return {
db: db,
callers: {
authed: createApiCaller({
...emptyContextMethods,
userAgent: 'TestUserAgent',
user: testUser,
db,
}),
notAuthed: createApiCaller({
...emptyContextMethods,
userAgent: undefined,
user: undefined,
db,
}),
admin: createApiCaller({
...emptyContextMethods,
userAgent: 'TestAdminUserAgent',
user: testAdminUser,
db
})
}
}
}
const exampleContextSoICanInferTyping = await createTestContext(true)
export type TestContextType = typeof exampleContextSoICanInferTyping
export type TypedTestFn = TestFn<TestContextType> I have the following relevant stuff in package.json: {
"name": "@bookd/bookd-api",
"version": "0.0.1",
"description": "book'd API Library",
"main": "app.ts",
"type": "module",
"ava": {
"extensions": {
"ts": "module",
"js": true
},
"nodeArguments": [
"--loader=tsx"
]
},
"scripts": {
"build": "tsup ./app.ts --format esm --dts",
"dev": "tsx watch ./app.ts",
"start": "tsc && npm run dev",
"test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava ./tests/runTests.test.ts",
"lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0",
"studio": "drizzle-kit studio --port 5666 --verbose --config ./db/drizzle.config.ts ",
"migration:push": "tsx ./db/migrate.ts",
"migration:generate": "drizzle-kit generate:pg --schema ./db/schema --out ./migrations"
},
"author": "",
"license": "ISC",
"dependencies": {
"@trpc/client": "^10.38.3",
"@trpc/server": "^10.38.3",
"argon2": "^0.31.2",
"colors": "^1.4.0",
"cookie": "^0.6.0",
"cors": "^2.8.5",
"drizzle-orm": "^0.29.1",
"drizzle-zod": "^0.5.1",
"jsonwebtoken": "^9.0.2",
"ms": "^2.1.3",
"pg": "^8.11.3",
"postgres": "3.3.5",
"superjson": "^2.2.1",
"ulid": "2.3.0",
"zod": "^3.22.2"
},
"devDependencies": {
"@types/cookie": "^0.6.0",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.17",
"@types/jsonwebtoken": "^9.0.5",
"@types/ms": "^0.7.34",
"@types/node": "^20.6.0",
"@types/pg": "^8.10.9",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
"ava": "^6.0.1",
"dotenv": "16.3.1",
"drizzle-kit": "^0.20.6",
"eslint": "^8.50.0",
"eslint-config-custom": "*",
"ts-node": "^10.9.2",
"tsconfig": "*",
"tsup": "^8.0.1",
"tsx": "^4.6.2",
"typescript": "^5.2.2"
}
}
node version:
Hope this helps. Let me know if you need more info from me. I can reproduce this 100% of times. |
Beta Was this translation helpful? Give feedback.
-
I'm also getting this with AVA v6.0.1 and worker threads disabled. I'm using Puppeteer as well, but am closing the browser in an async Does anybody have a workaround? |
Beta Was this translation helpful? Give feedback.
-
I'm experiencing this too - has anyone figured out a way to solve this? |
Beta Was this translation helpful? Give feedback.
-
I get this on every run, |
Beta Was this translation helpful? Give feedback.
-
Any updates on this? It happens to me too... |
Beta Was this translation helpful? Give feedback.
-
In response to the original post in this thread, AVA 6 removed the previous behavior of forcibly exiting the worker thread. This may cause problems with test environments that have open handles after tests complete. See https://github.com/avajs/ava/blob/main/docs/08-common-pitfalls.md#timeouts-because-a-file-failed-to-exit |
Beta Was this translation helpful? Give feedback.
This comment has been minimized.
This comment has been minimized.
-
Beta Was this translation helpful? Give feedback.
This has shipped in AVA 6.