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.