1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-07 01:16:28 +02:00

task: migrate tests to vitest

Vitest Pros:
* Automated failing test comments on github PRs
* A nice local UI with incremental testing when changing files (`yarn
test:ui`)
* Also nicely supported in all major IDEs, click to run test works (so
we won't miss what we had with jest).
* Works well with ESM

Vitest Cons:
* The ESBuild transformer vitest uses takes a little longer to transform
than our current SWC/jest setup, however, it is possible to setup SWC as
the transformer for vitest as well (though it only does one transform,
so we're paying ~7-10 seconds instead of ~ 2-3 seconds in transform
phase).
* Exposes how slow our tests are (tongue in cheek here)
This commit is contained in:
Christopher Kolstad 2025-05-16 11:19:10 +02:00 committed by GitHub
parent 4d1b44818f
commit b681702b77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
119 changed files with 2324 additions and 96108 deletions

View File

@ -16,7 +16,10 @@ jobs:
build:
runs-on: ubuntu-latest
name: build
permissions:
contents: read
# Needed for the github-reporter from vite to make comments on PRs
pull-requests: write
services:
# Label used to access the service container
postgres:
@ -47,14 +50,8 @@ jobs:
YARN_ENABLE_SCRIPTS: false
- run: yarn lint
- run: yarn build:backend
- run: yarn run test:report # This adds test results as github check to the workflow
- run: yarn run test
env:
CI: true
TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
- name: Upload test report to build # Done this way since external PRs would not be able to write the check. See https://github.com/marketplace/actions/test-reporter#recommended-setup-for-public-repositories
uses: actions/upload-artifact@v4
if: (success() || failure()) && github.ref == 'refs/heads/main'
with:
name: test-results
path: ./reports/jest-junit.xml

View File

@ -12,6 +12,9 @@ on:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
pull_requests: write
name: build # temporary solution to trick branch protection rules
services:
@ -32,31 +35,25 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Use Node.js 20.x
- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: 'yarn'
- name: Enable corepack
run: corepack enable
- run: yarn build:backend
- name: Tests on 20.x
- name: Install deps
run: yarn install
- name: Build backend
run: yarn backend
- name: Tests on 22.x
id: coverage
uses: ArtiomTr/jest-coverage-report-action@v2
with:
annotations: none
package-manager: yarn
test-script: yarn run test:coverage:jest
base-coverage-file: ./coverage/report.json
output: report-markdown
run: yarn test:coverage
env:
CI: true
TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
NODE_ENV: test
PORT: 4243
# - name: Report coverage on ${{ matrix.node-version }}
# uses: marocchino/sticky-pull-request-comment@v2
# with:
# # pass output from the previous step by id.
# message: ${{ steps.coverage.outputs.report }}
- name: Report coverage
uses: davelosert/vitest-coverage-report-action@v2

1
.gitignore vendored
View File

@ -107,3 +107,4 @@ website/.yarn/*
!website/.yarn/sdks
!website/.yarn/versions
coverage/.tmp

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -53,15 +53,15 @@
"local:package": "del-cli --force build && mkdir build && cp -r dist docs CHANGELOG.md LICENSE README.md package.json build",
"build:watch": "tsc -w",
"prepare": "husky && yarn --cwd ./frontend install && if [ ! -d ./dist ]; then yarn build; fi",
"test": "NODE_ENV=test PORT=4243 node --experimental-vm-modules node_modules/.bin/jest",
"test:unit": "NODE_ENV=test PORT=4243 node --experimental-vm-modules node_modules/.bin/jest --testPathIgnorePatterns=src/test/e2e",
"test": "NODE_ENV=test PORT=4243 vitest run",
"test:unit": "NODE_ENV=test PORT=4243 vitest --exclude src/test/e2e",
"test:docker": "./scripts/docker-postgres.sh",
"test:report": "NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test PORT=4243 node node_modules/.bin/jest --reporters=\"default\" --reporters=\"jest-junit\"",
"test:report": "NODE_ENV=test PORT=4243 vitest --reporter=junit",
"test:docker:cleanup": "docker rm -f unleash-postgres",
"test:watch": "yarn test --watch",
"test:coverage": "NODE_ENV=test PORT=4243 node --experimental-vm-modules node_modules/.bin/jest --coverage --testLocationInResults --outputFile=\"coverage/report.json\" --forceExit",
"test:coverage:jest": "NODE_ENV=test PORT=4243 node --experimental-vm-modules node_modules/.bin/jest --silent --ci --json --coverage --testLocationInResults --outputFile=\"report.json\" --forceExit",
"test:updateSnapshot": "NODE_ENV=test PORT=4243 node --experimental-vm-modules node_modules/.bin/jest --updateSnapshot",
"test:watch": "vitest",
"test:coverage": "NODE_ENV=test PORT=4243 vitest run --coverage --outputFile=\"coverage/report.json\"",
"test:updateSnapshot": "NODE_ENV=test PORT=4243 vitest run -u",
"test:ui": "vitest --ui",
"seed:setup": "ts-node src/test/e2e/seed/segment.seed.ts",
"seed:serve": "UNLEASH_DATABASE_NAME=unleash_test UNLEASH_DATABASE_SCHEMA=seed yarn run start:dev",
"clean": "del-cli --force dist",
@ -69,66 +69,6 @@
"prepack": "./scripts/prepack.sh",
"schema:update": "node ./.husky/update-openapi-spec-list.js"
},
"jest-junit": {
"suiteName": "Unleash Unit Tests",
"outputDirectory": "./reports",
"outputName": "jest-junit.xml",
"uniqueOutputName": "false",
"classNameTemplate": "{classname}-{title}",
"titleTemplate": "{classname}-{title}",
"ancestorSeparator": " ",
"usePathForSuiteName": "true"
},
"jest": {
"automock": false,
"maxWorkers": 4,
"testTimeout": 20000,
"globalSetup": "./src/jest-setup.ts",
"transform": {
"^.+\\.tsx?$": [
"@swc/jest",
{
"jsc": {
"target": "ESNext",
"parser": {
"syntax": "typescript"
}
},
"isModule": true
}
]
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"testPathIgnorePatterns": [
"/dist/",
"/node_modules/",
"/frontend/",
"/website/"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/dist/",
"/src/migrations",
"/src/test"
],
"extensionsToTreatAsEsm": [
".ts",
".tsx"
],
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
},
"transformIgnorePatterns": [
"node_modules"
]
},
"dependencies": {
"@slack/web-api": "^7.9.1",
"@wesleytodd/openapi": "^1.1.0",
@ -199,15 +139,14 @@
"@babel/core": "7.26.10",
"@biomejs/biome": "^1.9.4",
"@cyclonedx/yarn-plugin-cyclonedx": "^2.0.0",
"@fast-check/vitest": "^0.2.1",
"@swc/core": "1.11.24",
"@swc/jest": "0.2.38",
"@types/bcryptjs": "2.4.6",
"@types/cors": "2.8.17",
"@types/express": "4.17.21",
"@types/express-session": "1.18.1",
"@types/faker": "5.5.9",
"@types/hash-sum": "^1.0.0",
"@types/jest": "29.5.14",
"@types/js-yaml": "4.0.9",
"@types/lodash.groupby": "4.6.9",
"@types/lodash.isequal": "^4.5.8",
@ -226,6 +165,8 @@
"@types/supertest": "6.0.2",
"@types/type-is": "1.6.7",
"@types/uuid": "9.0.8",
"@vitest/coverage-v8": "^3.1.3",
"@vitest/ui": "^3.1.3",
"concurrently": "^9.0.0",
"copyfiles": "2.4.1",
"coveralls": "^3.1.1",
@ -234,8 +175,6 @@
"fast-check": "3.23.2",
"fetch-mock": "^12.0.0",
"husky": "^9.0.11",
"jest": "29.7.0",
"jest-junit": "^16.0.0",
"lint-staged": "15.4.3",
"nock": "13.5.6",
"openapi-enforcer": "1.23.0",
@ -247,6 +186,7 @@
"tsc-watch": "6.2.1",
"typescript": "5.8.3",
"vite-node": "^3.1.3",
"vitest": "^3.1.3",
"wait-on": "^8.0.0"
},
"resolutions": {

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`should create default config 1`] = `
{

View File

@ -1,15 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Datadog integration Should call datadog webhook for archived toggle 1`] = `"{"text":"%%% \\n *some@user.com* archived *some-toggle* in project ** \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should call datadog webhook for archived toggle 1`] = `"{"text":"%%% \\n *some@user.com* archived *some-toggle* in project ** \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration Should call datadog webhook for archived toggle with project info 1`] = `"{"text":"%%% \\n *some@user.com* archived *some-toggle* in project *[some-project](http://some-url.com/projects/some-project)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should call datadog webhook for archived toggle with project info 1`] = `"{"text":"%%% \\n *some@user.com* archived *some-toggle* in project *[some-project](http://some-url.com/projects/some-project)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration Should call datadog webhook 1`] = `"{"text":"%%% \\n *some@user.com* created *[some-toggle](http://some-url.com/projects//features/some-toggle)* in project ** \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should call datadog webhook 1`] = `"{"text":"%%% \\n *some@user.com* created *[some-toggle](http://some-url.com/projects//features/some-toggle)* in project ** \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration Should call datadog webhook for toggled environment 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should call datadog webhook for toggled environment 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration Should call datadog webhook with JSON when template set 1`] = `"{"text":"{\\n \\"event\\": \\"feature-created\\",\\n \\"createdBy\\": \\"some@user.com\\"\\n}","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should call datadog webhook with JSON when template set 1`] = `"{"text":"{\\n \\"event\\": \\"feature-created\\",\\n \\"createdBy\\": \\"some@user.com\\"\\n}","title":"Unleash notification update"}"`;
exports[`Datadog integration Should include customHeaders in headers when calling service 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration > Should include customHeaders in headers when calling service 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update"}"`;
exports[`Datadog integration Should not include source_type_name when included in the config 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update","source_type_name":"my-custom-source-type"}"`;
exports[`Datadog integration > Should not include source_type_name when included in the config 1`] = `"{"text":"%%% \\n *some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)* \\n %%% ","title":"Unleash notification update","source_type_name":"my-custom-source-type"}"`;

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Should format specialised text for events when IPs changed 1`] = `
{

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Slack integration Should call slack webhook 1`] = `
exports[`Slack integration > Should call slack webhook 1`] = `
{
"attachments": [
{
@ -23,10 +23,10 @@ exports[`Slack integration Should call slack webhook 1`] = `
}
`;
exports[`Slack integration Should call slack webhook for archived toggle 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* archived *some-toggle* in project **","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects//archive"}]}]}"`;
exports[`Slack integration > Should call slack webhook for archived toggle 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* archived *some-toggle* in project **","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects//archive"}]}]}"`;
exports[`Slack integration Should call slack webhook for archived toggle with project info 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* archived *some-toggle* in project *<http://some-url.com/projects/some-project|some-project>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/some-project/archive"}]}]}"`;
exports[`Slack integration > Should call slack webhook for archived toggle with project info 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* archived *some-toggle* in project *<http://some-url.com/projects/some-project|some-project>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/some-project/archive"}]}]}"`;
exports[`Slack integration Should call webhook for toggled environment 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* disabled *<http://some-url.com/projects/default/features/some-toggle|some-toggle>* for the *development* environment in project *<http://some-url.com/projects/default|default>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Slack integration > Should call webhook for toggled environment 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* disabled *<http://some-url.com/projects/default/features/some-toggle|some-toggle>* for the *development* environment in project *<http://some-url.com/projects/default|default>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Slack integration Should include custom headers from parameters in call to service 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* disabled *<http://some-url.com/projects/default/features/some-toggle|some-toggle>* for the *development* environment in project *<http://some-url.com/projects/default|default>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Slack integration > Should include custom headers from parameters in call to service 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"*some@user.com* disabled *<http://some-url.com/projects/default/features/some-toggle|some-toggle>* for the *development* environment in project *<http://some-url.com/projects/default|default>*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;

View File

@ -1,11 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Teams integration Should call teams webhook 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* created *[some-toggle](http://some-url.com/projects//features/some-toggle)* in project **","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-created"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects//features/some-toggle"}]}]}"`;
exports[`Teams integration > Should call teams webhook 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* created *[some-toggle](http://some-url.com/projects//features/some-toggle)* in project **","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-created"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects//features/some-toggle"}]}]}"`;
exports[`Teams integration Should call teams webhook for archived toggle 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* archived *some-toggle* in project **","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-archived"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects//archive"}]}]}"`;
exports[`Teams integration > Should call teams webhook for archived toggle 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* archived *some-toggle* in project **","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-archived"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects//archive"}]}]}"`;
exports[`Teams integration Should call teams webhook for archived toggle with project info 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* archived *some-toggle* in project *[some-project](http://some-url.com/projects/some-project)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-archived"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/some-project/archive"}]}]}"`;
exports[`Teams integration > Should call teams webhook for archived toggle with project info 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* archived *some-toggle* in project *[some-project](http://some-url.com/projects/some-project)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-archived"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/some-project/archive"}]}]}"`;
exports[`Teams integration Should call teams webhook for toggled environment 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Teams integration > Should call teams webhook for toggled environment 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Teams integration Should include custom headers in call to teams 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;
exports[`Teams integration > Should include custom headers in call to teams 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"*some@user.com* disabled *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* for the *development* environment in project *[default](http://some-url.com/projects/default)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`;

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Webhook integration should allow for eventJson and eventMarkdown in bodyTemplate 1`] = `
exports[`Webhook integration > should allow for eventJson and eventMarkdown in bodyTemplate 1`] = `
"{
"json": "{\\"id\\":1,\\"createdAt\\":\\"2024-07-24T00:00:00.000Z\\",\\"createdByUserId\\":-1337,\\"type\\":\\"feature-created\\",\\"createdBy\\":\\"some@user.com\\",\\"featureName\\":\\"some-toggle\\",\\"project\\":\\"default\\",\\"data\\":{\\"name\\":\\"some-toggle\\",\\"enabled\\":false,\\"strategies\\":[{\\"name\\":\\"default\\"}]}}",
"markdown": "*some@user.com* created *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* in project *[default](http://some-url.com/projects/default)*"

View File

@ -16,17 +16,17 @@ import {
} from '../types/index.js';
import type { IntegrationEventsService } from '../services/index.js';
import nock from 'nock';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
const INTEGRATION_ID = 1337;
const ARGS: IAddonConfig = {
getLogger: noLogger,
unleashUrl: 'http://some-url.com',
integrationEventsService: {
registerEvent: jest.fn(),
registerEvent: vi.fn(),
} as unknown as IntegrationEventsService,
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
eventBus: <any>{ emit: jest.fn() },
eventBus: <any>{ emit: vi.fn() },
};
describe('Datadog integration', () => {
@ -317,7 +317,7 @@ describe('Datadog integration', () => {
test('Should call registerEvent', async () => {
const addon = new DatadogAddon(ARGS);
const registerEventSpy = jest.spyOn(addon, 'registerEvent');
const registerEventSpy = vi.spyOn(addon, 'registerEvent');
const event: IEvent = {
id: 1,
createdAt: new Date(),

View File

@ -17,12 +17,12 @@ import {
FEATURE_ARCHIVED,
FEATURE_ENVIRONMENT_DISABLED,
} from '../events/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import nock from 'nock';
const asyncGunzip = promisify(gunzip);
const registerEventMock = jest.fn();
const registerEventMock = vi.fn();
const INTEGRATION_ID = 1337;
const ARGS: IAddonConfig = {
@ -33,7 +33,7 @@ const ARGS: IAddonConfig = {
} as unknown as IntegrationEventsService,
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
eventBus: {
emit: jest.fn(),
emit: vi.fn(),
} as any,
};

View File

@ -14,18 +14,18 @@ import {
} from '../types/index.js';
import type { IntegrationEventsService } from '../services/index.js';
const slackApiCalls: ChatPostMessageArguments[] = [];
import { jest } from '@jest/globals';
import { vi } from 'vitest';
const loggerMock = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
fatal: vi.fn(),
};
const getLogger = jest.fn(() => loggerMock);
const getLogger = vi.fn(() => loggerMock);
const registerEventMock = jest.fn();
const registerEventMock = vi.fn();
const INTEGRATION_ID = 1337;
const ARGS: IAddonConfig = {
@ -36,11 +36,11 @@ const ARGS: IAddonConfig = {
} as unknown as IntegrationEventsService,
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
eventBus: {
emit: jest.fn(),
emit: vi.fn(),
} as any,
};
let postMessage = jest.fn().mockImplementation((options: any) => {
let postMessage = vi.fn().mockImplementation((options: any) => {
slackApiCalls.push(options);
return Promise.resolve();
});
@ -73,7 +73,7 @@ describe('SlackAppAddon', () => {
};
beforeEach(() => {
jest.useFakeTimers();
vi.useFakeTimers();
slackApiCalls.length = 0;
postMessage.mockClear();
registerEventMock.mockClear();
@ -82,13 +82,13 @@ describe('SlackAppAddon', () => {
chat: {
postMessage,
},
on: jest.fn(),
on: vi.fn(),
} as unknown as Methods;
});
});
afterEach(() => {
jest.useRealTimers();
vi.useRealTimers();
});
it('should post message when feature is toggled', async () => {
@ -182,7 +182,7 @@ describe('SlackAppAddon', () => {
it('should log error when an API call fails', async () => {
// @ts-ignore
postMessage = jest.fn().mockRejectedValue(mockError);
postMessage = vi.fn().mockRejectedValue(mockError);
await addon.handleEvent(
event,
@ -208,13 +208,10 @@ describe('SlackAppAddon', () => {
],
};
postMessage = jest
postMessage = vi
.fn()
// @ts-ignore
.mockResolvedValueOnce({ ok: true })
// @ts-ignore
.mockResolvedValueOnce({ ok: true })
// @ts-ignore
.mockRejectedValueOnce(mockError);
await addon.handleEvent(

View File

@ -17,10 +17,10 @@ import {
} from '../types/index.js';
import type { IntegrationEventsService } from '../services/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import nock from 'nock';
const registerEventMock = jest.fn();
const registerEventMock = vi.fn();
const INTEGRATION_ID = 1337;
const ARGS: IAddonConfig = {
@ -31,7 +31,7 @@ const ARGS: IAddonConfig = {
} as unknown as IntegrationEventsService,
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
eventBus: {
emit: jest.fn(),
emit: vi.fn(),
} as any,
};
describe('Slack integration', () => {

View File

@ -17,10 +17,10 @@ import {
} from '../types/index.js';
import type { IntegrationEventsService } from '../services/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import nock from 'nock';
const registerEventMock = jest.fn();
const registerEventMock = vi.fn();
const INTEGRATION_ID = 1337;
const ARGS: IAddonConfig = {
@ -31,7 +31,7 @@ const ARGS: IAddonConfig = {
} as unknown as IntegrationEventsService,
flagResolver: { isEnabled: (expName: IFlagKey) => false } as IFlagResolver,
eventBus: {
emit: jest.fn(),
emit: vi.fn(),
} as any,
};

View File

@ -12,7 +12,7 @@ import {
} from '../types/index.js';
import type { IntegrationEventsService } from '../services/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import EventEmitter from 'node:events';
import nock from 'nock';
@ -28,7 +28,7 @@ afterEach(() => {
});
const setup = () => {
const registerEventMock = jest.fn();
const registerEventMock = vi.fn();
const addonConfig: IAddonConfig = {
getLogger: noLogger,
unleashUrl: 'http://some-url.com',

View File

@ -1,9 +1,9 @@
import { createTestConfig } from '../test/config/test-config.js';
import { jest } from '@jest/globals';
import { type Mock, vi } from 'vitest';
// This mock setup MUST be at the top-level, before any other code that might trigger imports.
jest.unstable_mockModule('compression', () => ({
default: jest.fn().mockImplementation(() => {
vi.mock('compression', () => ({
default: vi.fn().mockImplementation(() => {
// This is the actual middleware function Express would use
return (req, res, next) => {
next();
@ -15,8 +15,8 @@ let compression: any;
const openApiService = {
// returns a middleware
validPath: jest.fn().mockReturnValue(() => {}),
useDocs: jest.fn(),
validPath: vi.fn().mockReturnValue(() => {}),
useDocs: vi.fn(),
};
const appModule = await import('./app.js');
@ -52,10 +52,10 @@ test('should call preRouterHook', async () => {
describe('compression middleware', () => {
beforeAll(async () => {
jest.resetModules(); // Crucial: Clears the module cache.
vi.resetModules(); // Crucial: Clears the module cache.
// Import 'compression' AFTER resetModules. This ensures we get the mock.
const compressionModule = await import('compression');
compression = compressionModule.default; // `compression` is now our jest.fn()
compression = compressionModule.default; // `compression` is now our vi.fn()
// Import 'app.js' AFTER resetModules and AFTER 'compression' is set to the mock.
// This ensures app.js uses the mocked version of compression.
@ -67,9 +67,9 @@ describe('compression middleware', () => {
// Clear call history for the mock before each test in this describe block
if (
compression &&
typeof (compression as jest.Mock).mockClear === 'function'
typeof (compression as Mock).mockClear === 'function'
) {
(compression as jest.Mock).mockClear();
(compression as Mock).mockClear();
} else {
// This case might happen if beforeAll failed or types are unexpected
console.warn(

View File

@ -5,6 +5,7 @@ import type { AccessStore } from './access-store.js';
import { BadDataError } from '../error/index.js';
let db: ITestDb;
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
beforeAll(async () => {
db = await dbInit('access_store_serial', getLogger);

View File

@ -5,10 +5,10 @@ import {
throwExceedsLimitError,
} from './exceeds-limit-error.js';
import { jest } from '@jest/globals';
import { vi, it, expect } from 'vitest';
it('emits events event when created through the external function', () => {
const emitEvent = jest.fn();
const emitEvent = vi.fn();
const resource = 'some-resource';
const limit = 10;
@ -31,7 +31,7 @@ it('emits events event when created through the external function', () => {
});
it('emits uses the resourceNameOverride for the event when provided, but uses the resource for the error', () => {
const emitEvent = jest.fn();
const emitEvent = vi.fn();
const resource = 'not this';
const resourceNameOverride = 'but this!';
const limit = 10;
@ -47,7 +47,9 @@ it('emits uses the resourceNameOverride for the event when provided, but uses th
limit,
},
),
).toThrow(new ExceedsLimitError(resource, limit));
).toThrowError(
expect.errorWithMessage(new ExceedsLimitError(resource, limit)),
);
expect(emitEvent).toHaveBeenCalledWith(EXCEEDS_LIMIT, {
resource: resourceNameOverride,

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`should match snapshot from /api/client/features 1`] = `
{

View File

@ -11,7 +11,7 @@ import type { Application } from 'express';
import type { IFlagResolver } from '../../../types/index.js';
import type TestAgent from 'supertest/lib/agent.d.ts';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let app: Application;
@ -78,10 +78,10 @@ test('should get empty getFeatures via client', () => {
});
test('if caching is enabled should memoize', async () => {
const getClientFeatures = jest.fn().mockReturnValue([]);
const getActiveSegmentsForClient = jest.fn().mockReturnValue([]);
const respondWithValidation = jest.fn().mockReturnValue({});
const validPath = jest.fn().mockReturnValue(jest.fn());
const getClientFeatures = vi.fn().mockReturnValue([]);
const getActiveSegmentsForClient = vi.fn().mockReturnValue([]);
const respondWithValidation = vi.fn().mockReturnValue({});
const validPath = vi.fn().mockReturnValue(vi.fn());
const clientSpecService = new ClientSpecService({ getLogger });
const openApiService = { respondWithValidation, validPath };
const clientFeatureToggleService = {
@ -119,10 +119,10 @@ test('if caching is enabled should memoize', async () => {
});
test('if caching is not enabled all calls goes to service', async () => {
const getClientFeatures = jest.fn().mockReturnValue([]);
const getActiveSegmentsForClient = jest.fn().mockReturnValue([]);
const respondWithValidation = jest.fn().mockReturnValue({});
const validPath = jest.fn().mockReturnValue(jest.fn());
const getClientFeatures = vi.fn().mockReturnValue([]);
const getActiveSegmentsForClient = vi.fn().mockReturnValue([]);
const respondWithValidation = vi.fn().mockReturnValue({});
const validPath = vi.fn().mockReturnValue(vi.fn());
const clientSpecService = new ClientSpecService({ getLogger });
const clientFeatureToggleService = {
getClientFeatures,

View File

@ -11,7 +11,7 @@ import type { ProjectAccess } from '../private-project/privateProjectStore.js';
import EventService, { filterAccessibleProjects } from './event-service.js';
import { type IBaseEvent, USER_UPDATED } from '../../events/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
describe('filterPrivateProjectsFromParams', () => {
it('should return IS_ANY_OF with allowed projects when projectParam is undefined and mode is limited', () => {
@ -124,13 +124,13 @@ describe('storeEvents', () => {
'should store the event %s',
async (preDataAndData: Pick<IBaseEvent, 'preData' | 'data'>) => {
const eventStore = {
batchStore: jest.fn(),
batchStore: vi.fn(),
} as unknown as IEventStore;
const eventService = new EventService(
{
eventStore,
featureTagStore: {
getAllByFeatures: jest.fn().mockReturnValue([]),
getAllByFeatures: vi.fn().mockReturnValue([]),
} as unknown as IFeatureTagStore,
},
{ getLogger, eventBus: undefined } as unknown as IUnleashConfig,
@ -152,13 +152,13 @@ describe('storeEvents', () => {
);
test('should not store the event when predata and data are the same', async () => {
const eventStore = {
batchStore: jest.fn(),
batchStore: vi.fn(),
} as unknown as IEventStore;
const eventService = new EventService(
{
eventStore,
featureTagStore: {
getAllByFeatures: jest.fn().mockReturnValue([]),
getAllByFeatures: vi.fn().mockReturnValue([]),
} as unknown as IFeatureTagStore,
},
{ getLogger, eventBus: undefined } as unknown as IUnleashConfig,

View File

@ -99,7 +99,7 @@ describe('validate incoming feature naming data', () => {
});
if (result.state === 'valid') {
fail('Expected invalid result');
expect.fail('Expected invalid result');
}
expect(result.reasons.length).toBe(1);

View File

@ -32,7 +32,14 @@ import {
import { insertLastSeenAt } from '../../../../test/e2e/helpers/test-helper.js';
import type { EventService } from '../../../services/index.js';
import type FeatureLinkService from '../../feature-links/feature-link-service.js';
import {
beforeAll,
afterAll,
beforeEach,
test,
expect,
describe,
} from 'vitest';
let stores: IUnleashStores;
let db: ITestDb;
let service: FeatureToggleService;
@ -385,7 +392,7 @@ test('cloning a feature flag not allowed for change requests enabled', async ()
SYSTEM_USER_AUDIT,
true,
),
).rejects.toEqual(
).rejects.errorWithMessage(
new ForbiddenError(
`Cloning not allowed. Project default has change requests enabled.`,
),
@ -399,7 +406,7 @@ test('changing to a project with change requests enabled should not be allowed',
});
await expect(
service.changeProject('newFlagName', 'default', TEST_AUDIT_USER),
).rejects.toEqual(
).rejects.errorWithMessage(
new ForbiddenError(
`Changing project not allowed. Project default has change requests enabled.`,
),
@ -531,7 +538,9 @@ test('If change requests are enabled, cannot change variants without going via C
TEST_AUDIT_USER,
[],
),
).rejects.toThrowError(new PermissionError(SKIP_CHANGE_REQUEST));
).rejects.toThrowError(
expect.errorWithMessage(new PermissionError(SKIP_CHANGE_REQUEST)),
);
});
test('If CRs are protected for any environment in the project stops bulk update of variants', async () => {
@ -620,7 +629,9 @@ test('If CRs are protected for any environment in the project stops bulk update
},
TEST_AUDIT_USER,
),
).rejects.toThrowError(new PermissionError(SKIP_CHANGE_REQUEST));
).rejects.toThrowError(
expect.errorWithMessage(new PermissionError(SKIP_CHANGE_REQUEST)),
);
});
test('getPlaygroundFeatures should return ids and titles (if they exist) on client strategies', async () => {
@ -799,7 +810,7 @@ test('Should not allow to add flags to archived projects', async () => {
},
TEST_AUDIT_USER,
),
).rejects.toEqual(
).rejects.errorWithMessage(
new NotFoundError(
`Active project with id archivedProject does not exist`,
),
@ -828,7 +839,7 @@ test('Should not allow to revive flags to archived projects', async () => {
await expect(
service.reviveFeature(flag.name, TEST_AUDIT_USER),
).rejects.toEqual(
).rejects.errorWithMessage(
new NotFoundError(
`Active project with id archivedProjectWithFlag does not exist`,
),
@ -836,7 +847,7 @@ test('Should not allow to revive flags to archived projects', async () => {
await expect(
service.reviveFeatures([flag.name], project.id, TEST_AUDIT_USER),
).rejects.toEqual(
).rejects.errorWithMessage(
new NotFoundError(
`Active project with id archivedProjectWithFlag does not exist`,
),

View File

@ -9,7 +9,7 @@ import type {
} from '../../../types/index.js';
import getLogger from '../../../../test/fixtures/no-logger.js';
import { ExceedsLimitError } from '../../../error/exceeds-limit-error.js';
import { describe, test, expect } from 'vitest';
const alwaysOnFlagResolver = {
isEnabled() {
return true;
@ -43,7 +43,7 @@ describe('Strategy limits', () => {
await addStrategy();
}
await expect(addStrategy()).rejects.toThrow(
await expect(addStrategy()).rejects.toThrowError(
"Failed to create strategy. You can't create more than the established limit of 3",
);
});
@ -87,7 +87,7 @@ describe('Strategy limits', () => {
contextName: 'accountId',
},
]),
).rejects.toThrow(
).rejects.toThrowError(
"Failed to create constraints. You can't create more than the established limit of 1",
);
});
@ -159,7 +159,7 @@ describe('Strategy limits', () => {
// check that you can't save more constraints
await expect(async () =>
updateStrategy([...constraints, ...constraints]),
).rejects.toThrow(new ExceedsLimitError('constraints', LIMIT));
).rejects.errorWithMessage(new ExceedsLimitError('constraints', LIMIT));
});
test('Should not allow to exceed constraint values limit', async () => {
@ -195,7 +195,7 @@ describe('Strategy limits', () => {
values: ['1', '2', '3', '4'],
},
]),
).rejects.toThrow(
).rejects.toThrowError(
"Failed to create constraint values for userId. You can't create more than the established limit of 3",
);
});
@ -266,7 +266,7 @@ describe('Strategy limits', () => {
// check that you can't save more constraint values
await expect(async () =>
updateStrategy(initialConstraintValueCount + 1),
).rejects.toThrow(
).rejects.errorWithMessage(
new ExceedsLimitError('constraint values for appName', LIMIT),
);
});

View File

@ -33,7 +33,7 @@ import type {
SetStrategySortOrderSchema,
} from '../../../openapi/index.js';
import { ForbiddenError } from '../../../error/index.js';
import { beforeAll, afterEach, afterAll, test, describe, expect } from 'vitest';
let app: IUnleashTest;
let db: ITestDb;
let defaultToken: IApiToken;
@ -282,7 +282,7 @@ test('should not allow to change project with dependencies', async () => {
'default',
TEST_AUDIT_USER,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new ForbiddenError(
'Changing project not allowed. Feature has dependencies.',
),
@ -2329,7 +2329,7 @@ test('Should not allow changing project to target project without the same enabl
'default',
TEST_AUDIT_USER,
),
).rejects.toThrow(new IncompatibleProjectError(targetProject));
).rejects.errorWithMessage(new IncompatibleProjectError(targetProject));
});
test('Should allow changing project to target project with the same enabled environments', async () => {
@ -2401,7 +2401,7 @@ test('Should allow changing project to target project with the same enabled envi
environment: '*',
secret: 'a',
});
await expect(async () =>
await expect(
app.services.projectService.changeProject(
targetProject,
featureName,

View File

@ -9,7 +9,7 @@ import getLogger from '../../../test/fixtures/no-logger.js';
import { randomId } from '../../util/index.js';
import { ApiTokenType } from '../../types/model.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let app: IUnleashNoSupertest;
let db: ITestDb;
@ -37,7 +37,7 @@ beforeAll(async () => {
afterEach(() => {
app.services.frontendApiService.stopAll();
jest.clearAllMocks();
vi.clearAllMocks();
});
afterAll(async () => {

View File

@ -21,7 +21,7 @@ import {
} from '../../types/index.js';
import type { FrontendApiService } from './frontend-api-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let app: IUnleashTest;
let db: ITestDb;
@ -42,7 +42,7 @@ beforeAll(async () => {
afterEach(() => {
app.services.frontendApiService.stopAll();
jest.clearAllMocks();
vi.clearAllMocks();
});
afterAll(async () => {
@ -362,7 +362,7 @@ test('should store frontend api client metrics', async () => {
});
// @ts-expect-error - cachedFeatureNames is a private property in ClientMetricsServiceV2
app.services.clientMetricsServiceV2.cachedFeatureNames = jest
app.services.clientMetricsServiceV2.cachedFeatureNames = vi
.fn<() => Promise<string[]>>()
.mockResolvedValue([featureName]);

View File

@ -14,7 +14,7 @@ import type {
} from '../../types/index.js';
import { UPDATE_REVISION } from '../feature-toggle/configuration-revision-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
const state = async (
cache: GlobalFrontendApiCache,
@ -53,7 +53,7 @@ const createCache = (
const config = {
getLogger: noLogger,
flagResolver: alwaysOnFlagResolver,
eventBus: <any>{ emit: jest.fn() },
eventBus: <any>{ emit: vi.fn() },
};
const segmentReadModel = new FakeSegmentReadModel([segment as ISegment]);
const clientFeatureToggleReadModel = new FakeClientFeatureToggleReadModel(

View File

@ -12,7 +12,7 @@ import type {
IUnleashStores,
} from '../../types/index.js';
import { createFakeGetLicensedUsers } from './getLicensedUsers.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let instanceStatsService: InstanceStatsService;
let versionService: VersionService;
@ -22,7 +22,7 @@ let flagResolver: IFlagResolver;
let updateMetrics: () => Promise<void>;
beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
register.clear();
@ -49,8 +49,8 @@ beforeEach(() => {
);
updateMetrics = collectAggDbMetrics;
jest.spyOn(clientInstanceStore, 'getDistinctApplicationsCount');
jest.spyOn(instanceStatsService, 'getStats');
vi.spyOn(clientInstanceStore, 'getDistinctApplicationsCount');
vi.spyOn(instanceStatsService, 'getStats');
expect(instanceStatsService.getStats).toHaveBeenCalledTimes(0);
});
@ -84,7 +84,7 @@ describe.each([true, false])(
'When feature enabled is %s',
(featureEnabled: boolean) => {
beforeEach(() => {
jest.spyOn(flagResolver, 'getVariant').mockReturnValue({
vi.spyOn(flagResolver, 'getVariant').mockReturnValue({
name: 'memorizeStats',
enabled: featureEnabled,
feature_enabled: featureEnabled,
@ -93,9 +93,7 @@ describe.each([true, false])(
test(`should${featureEnabled ? ' ' : ' not '}memoize query results`, async () => {
const segmentStore = stores.segmentStore;
jest.spyOn(segmentStore, 'count').mockReturnValue(
Promise.resolve(5),
);
vi.spyOn(segmentStore, 'count').mockReturnValue(Promise.resolve(5));
expect(segmentStore.count).toHaveBeenCalledTimes(0);
expect(await instanceStatsService.segmentCount()).toBe(5);
expect(segmentStore.count).toHaveBeenCalledTimes(1);
@ -107,7 +105,7 @@ describe.each([true, false])(
test(`should${featureEnabled ? ' ' : ' not '}memoize async query results`, async () => {
const trafficDataUsageStore = stores.trafficDataUsageStore;
jest.spyOn(
vi.spyOn(
trafficDataUsageStore,
'getTrafficDataUsageForPeriod',
).mockReturnValue(
@ -142,7 +140,7 @@ describe.each([true, false])(
test(`getStats should${featureEnabled ? ' ' : ' not '}be memorized`, async () => {
const featureStrategiesReadModel =
stores.featureStrategiesReadModel;
jest.spyOn(
vi.spyOn(
featureStrategiesReadModel,
'getMaxFeatureEnvironmentStrategies',
).mockReturnValue(

View File

@ -5,7 +5,7 @@ import { createTestConfig } from '../../../test/config/test-config.js';
import FakeSettingStore from '../../../test/fixtures/fake-setting-store.js';
import type EventService from '../events/event-service.js';
import { TEST_AUDIT_USER } from '../../types/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
test('Scheduler should run scheduled functions if maintenance mode is off', async () => {
const config = createTestConfig();
@ -20,7 +20,7 @@ test('Scheduler should run scheduled functions if maintenance mode is off', asyn
config.eventBus,
);
const job = jest.fn();
const job = vi.fn();
await schedulerService.schedule(
async () => {
@ -52,7 +52,7 @@ test('Scheduler should not run scheduled functions if maintenance mode is on', a
TEST_AUDIT_USER,
);
const job = jest.fn();
const job = vi.fn();
await schedulerService.schedule(
async () => {

View File

@ -13,13 +13,13 @@ import { endOfDay, startOfHour, subDays, subHours } from 'date-fns';
import type { IClientMetricsEnv } from './client-metrics-store-v2-type.js';
import { UnknownFlagsService } from '../unknown-flags/unknown-flags-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
function initClientMetrics() {
const stores = createStores();
const eventBus = new EventEmitter();
eventBus.emit = jest.fn(() => true);
eventBus.emit = vi.fn(() => true);
const config = {
eventBus,
@ -35,7 +35,7 @@ function initClientMetrics() {
},
config,
);
lastSeenService.updateLastSeen = jest.fn();
lastSeenService.updateLastSeen = vi.fn();
const unknownFlagsService = new UnknownFlagsService(
{
unknownFlagsStore: stores.unknownFlagsStore,
@ -56,7 +56,7 @@ test('process metrics properly', async () => {
const { clientMetricsService, eventBus, lastSeenService, stores } =
initClientMetrics();
stores.clientMetricsStoreV2.getFeatureFlagNames = jest
stores.clientMetricsStoreV2.getFeatureFlagNames = vi
.fn<() => Promise<string[]>>()
.mockResolvedValue(['myCoolToggle', 'myOtherToggle']);

View File

@ -12,15 +12,15 @@ import FakeStrategiesStore from '../../../../test/fixtures/fake-strategies-store
import FakeFeatureToggleStore from '../../feature-toggle/fakes/fake-feature-toggle-store.js';
import type { IApplicationOverview } from './models.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let config: IUnleashConfig;
beforeAll(() => {
config = createTestConfig({});
});
test('Multiple registrations of same appname and instanceid within same time period should only cause one registration', async () => {
const appStoreSpy = jest.fn();
const bulkSpy = jest.fn();
const appStoreSpy = vi.fn();
const bulkSpy = vi.fn();
const clientApplicationsStore: any = {
bulkUpsert: appStoreSpy,
};
@ -65,12 +65,12 @@ test('Multiple registrations of same appname and instanceid within same time per
expect(registrations[0].started).toBe(client1.started);
expect(registrations[0].interval).toBe(client1.interval);
jest.useRealTimers();
vi.useRealTimers();
});
test('Multiple unique clients causes multiple registrations', async () => {
const appStoreSpy = jest.fn();
const bulkSpy = jest.fn();
const appStoreSpy = vi.fn();
const bulkSpy = vi.fn();
const clientApplicationsStore: any = {
bulkUpsert: appStoreSpy,
};
@ -119,8 +119,8 @@ test('Multiple unique clients causes multiple registrations', async () => {
});
test('Same client registered outside of dedup interval will be registered twice', async () => {
const appStoreSpy = jest.fn();
const bulkSpy = jest.fn();
const appStoreSpy = vi.fn();
const bulkSpy = vi.fn();
const clientApplicationsStore: any = {
bulkUpsert: appStoreSpy,
};
@ -162,9 +162,7 @@ test('Same client registered outside of dedup interval will be registered twice'
expect(appStoreSpy).toHaveBeenCalledTimes(2);
expect(bulkSpy).toHaveBeenCalledTimes(2);
// @ts-expect-error unknown type
const firstRegistrations = appStoreSpy.mock.calls[0][0][0];
// @ts-expect-error unknown type
const secondRegistrations = appStoreSpy.mock.calls[1][0][0];
expect(firstRegistrations.appName).toBe(secondRegistrations.appName);
@ -172,8 +170,8 @@ test('Same client registered outside of dedup interval will be registered twice'
});
test('No registrations during a time period will not call stores', async () => {
const appStoreSpy = jest.fn();
const bulkSpy = jest.fn();
const appStoreSpy = vi.fn();
const bulkSpy = vi.fn();
const clientApplicationsStore: any = {
bulkUpsert: appStoreSpy,
};

View File

@ -3,13 +3,13 @@ import EventEmitter from 'events';
import getLogger from '../../../../../test/fixtures/no-logger.js';
import type { IUnleashConfig } from '../../../../types/index.js';
import { type LastSeenInput, LastSeenService } from '../last-seen-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
function initLastSeenService(flagEnabled = true) {
const stores = createStores();
const eventBus = new EventEmitter();
eventBus.emit = jest.fn() as () => boolean;
eventBus.emit = vi.fn() as () => boolean;
const config = {
eventBus,
@ -38,7 +38,7 @@ function initLastSeenService(flagEnabled = true) {
test('should not add duplicates per feature/environment', async () => {
const { lastSeenService, featureToggleStore, lastSeenStore } =
initLastSeenService(false);
const lastSeenSpy = jest.spyOn(lastSeenStore, 'setLastSeen');
const lastSeenSpy = vi.spyOn(lastSeenStore, 'setLastSeen');
lastSeenService.updateLastSeen([
{
@ -95,7 +95,7 @@ test('should call last seen at store with correct data', async () => {
timestamp: new Date(),
},
]);
lastSeenStore.setLastSeen = jest.fn() as (
lastSeenStore.setLastSeen = vi.fn() as (
data: LastSeenInput[],
) => Promise<void>;
await lastSeenService.store();

View File

@ -12,7 +12,7 @@ import { createTestConfig } from '../../../test/config/test-config.js';
import { createOnboardingService } from './createOnboardingService.js';
import type EventEmitter from 'events';
import { STAGE_ENTERED, USER_LOGIN } from '../../metric-events.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let db: ITestDb;
let stores: IUnleashStores;
@ -41,25 +41,25 @@ beforeEach(async () => {
await stores.projectStore.deleteAll();
await stores.onboardingStore.deleteAll();
await stores.userStore.deleteAll();
jest.useRealTimers();
vi.useRealTimers();
});
test('Default project should take first user created instead of project created as start time', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date());
vi.useFakeTimers();
vi.setSystemTime(new Date());
const { userStore, featureToggleStore, projectStore } = stores;
// default projects are created in advance and should be ignored
await projectStore.create({ id: 'default', name: 'irrelevant' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
const user = await userStore.insert({});
await featureToggleStore.create('default', {
name: 'test-default',
createdByUserId: user.id,
});
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({
type: 'flag-created',
flag: 'test-default',
@ -82,12 +82,12 @@ test('Default project should take first user created instead of project created
});
test('Ignore events for existing customers', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date(2024, 8, 2)); // day before we added metrics
vi.useFakeTimers();
vi.setSystemTime(new Date(2024, 8, 2)); // day before we added metrics
const { userStore } = stores;
await userStore.insert({});
jest.setSystemTime(new Date());
vi.setSystemTime(new Date());
await onboardingService.insert({ type: 'first-user-login' });
const { rows: instanceEvents } = await db.rawDatabase.raw(
@ -109,8 +109,8 @@ test('Ignore system user in onboarding events', async () => {
});
test('Storing onboarding events', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date());
vi.useFakeTimers();
vi.setSystemTime(new Date());
const { userStore, featureToggleStore, projectStore } = stores;
const user = await userStore.insert({});
await projectStore.create({ id: 'test_project', name: 'irrelevant' });
@ -119,23 +119,23 @@ test('Storing onboarding events', async () => {
createdByUserId: user.id,
});
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'first-user-login' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'second-user-login' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'flag-created', flag: 'test' });
await onboardingService.insert({ type: 'flag-created', flag: 'test' });
await onboardingService.insert({ type: 'flag-created', flag: 'invalid' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'pre-live', flag: 'test' });
await onboardingService.insert({ type: 'pre-live', flag: 'test' });
await onboardingService.insert({ type: 'pre-live', flag: 'invalid' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'live', flag: 'test' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'live', flag: 'test' });
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
await onboardingService.insert({ type: 'live', flag: 'invalid' });
const { rows: instanceEvents } = await db.rawDatabase.raw(
@ -174,8 +174,8 @@ const reachedOnboardingEvents = (count: number) => {
};
test('Reacting to events', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date());
vi.useFakeTimers();
vi.setSystemTime(new Date());
const { userStore, featureToggleStore, projectStore } = stores;
const user = await userStore.insert({});
await projectStore.create({ id: 'test_project', name: 'irrelevant' });
@ -183,7 +183,7 @@ test('Reacting to events', async () => {
name: 'test',
createdByUserId: user.id,
});
jest.advanceTimersByTime(minutesToMilliseconds(1));
vi.advanceTimersByTime(minutesToMilliseconds(1));
eventBus.emit(USER_LOGIN, { loginOrder: 0 });
eventBus.emit(USER_LOGIN, { loginOrder: 1 });

View File

@ -3,7 +3,8 @@ import fc from 'fast-check';
import supertest from 'supertest';
import { createServices } from '../../services/index.js';
import { createTestConfig } from '../../../test/config/test-config.js';
import { it } from '@fast-check/vitest';
import { describe } from 'vitest';
import createStores from '../../../test/fixtures/store.js';
import getApp from '../../app.js';
@ -80,7 +81,7 @@ describe('the playground API', () => {
.send(payload)
.expect('Content-Type', /json/);
return status === 400;
expect(status).toBe(400);
},
),
testParams,

View File

@ -12,7 +12,7 @@ import {
import NameExistsError from '../../error/name-exists-error.js';
import type { EventService } from '../../services/index.js';
import { createEventsService } from '../events/createEventsService.js';
import { test, beforeAll, afterAll, expect } from 'vitest';
let stores: IUnleashStores;
let db: ITestDb;
let service: EnvironmentService;
@ -219,25 +219,26 @@ test('Adding same environment twice should throw a NameExistsError', async () =>
'default',
SYSTEM_USER_AUDIT,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new NameExistsError(
'default already has the environment uniqueness-test enabled',
),
);
});
test('Removing environment not connected to project should be a noop', async () =>
expect(async () =>
test('Removing environment not connected to project should be a noop', async () => {
await expect(
service.removeEnvironmentFromProject(
'some-non-existing-environment',
'default',
SYSTEM_USER_AUDIT,
),
).resolves);
).resolves;
});
test('Trying to get an environment that does not exist throws NotFoundError', async () => {
const envName = 'this-should-not-exist';
await expect(async () => service.get(envName)).rejects.toThrow(
await expect(async () => service.get(envName)).rejects.errorWithMessage(
new NotFoundError(`Could not find environment with name: ${envName}`),
);
});

View File

@ -34,7 +34,14 @@ import { DEFAULT_ENV, extractAuditInfoFromUser } from '../../util/index.js';
import { ApiTokenType } from '../../types/model.js';
import { createApiTokenService } from '../api-tokens/createApiTokenService.js';
import type User from '../../types/user.js';
import {
beforeAll,
expect,
test,
beforeEach,
afterEach,
afterAll,
} from 'vitest';
let stores: IUnleashStores;
let db: ITestDb;
@ -672,7 +679,7 @@ describe('Managing Project access', () => {
[secondUser.id],
projectAuditUser,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new InvalidOperationError(
'User tried to grant role they did not have access to',
),
@ -746,7 +753,7 @@ describe('Managing Project access', () => {
[secondUser.id],
projectAuditUser,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new InvalidOperationError(
'User tried to grant role they did not have access to',
),
@ -868,7 +875,7 @@ describe('Managing Project access', () => {
[customRoleUpdateEnvironments.id],
auditProjectUser,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new InvalidOperationError(
'User tried to assign a role they did not have access to',
),
@ -885,7 +892,7 @@ describe('Managing Project access', () => {
[customRoleUpdateEnvironments.id],
auditProjectUser,
),
).rejects.toThrow(
).rejects.errorWithMessage(
new InvalidOperationError(
'User tried to assign a role they did not have access to',
),
@ -2641,12 +2648,12 @@ describe('create project with environments', () => {
});
test("envs that don't exist cause errors", async () => {
await expect(createProjectWithEnvs(['fake-project'])).rejects.toThrow(
BadDataError,
);
await expect(createProjectWithEnvs(['fake-project'])).rejects.toThrow(
/'fake-project'/,
);
await expect(
createProjectWithEnvs(['fake-project']),
).rejects.toThrowError(BadDataError);
await expect(
createProjectWithEnvs(['fake-project']),
).rejects.toThrowError(/'fake-project'/);
});
});

View File

@ -8,7 +8,7 @@ import {
} from '../../types/index.js';
import { createFakeProjectService } from './createProjectService.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
describe('enterprise extension: enable change requests', () => {
const createService = (mode: 'oss' | 'enterprise' = 'enterprise') => {
@ -75,7 +75,7 @@ describe('enterprise extension: enable change requests', () => {
test("it does not call the change request enablement function if we're not enterprise", async () => {
const { service } = createService('oss');
const fn = jest.fn() as () => Promise<
const fn = vi.fn() as () => Promise<
{ name: string; requiredApprovals: number }[] | undefined
>;
@ -334,7 +334,7 @@ describe('enterprise extension: enable change requests', () => {
const { service } = createService();
const projectId = 'fake-project-id';
expect(
await expect(
service.createProject(
{
id: projectId,
@ -357,7 +357,7 @@ describe('enterprise extension: enable change requests', () => {
const { service } = createService('oss');
const projectId = 'fake-project-id';
expect(
await expect(
service.createProject(
{
id: projectId,

View File

@ -8,7 +8,7 @@ import type EventService from '../events/event-service.js';
import { SCHEDULER_JOB_TIME } from '../../metric-events.js';
import EventEmitter from 'events';
import { TEST_AUDIT_USER } from '../../types/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
function ms(timeMs) {
return new Promise((resolve) => setTimeout(resolve, timeMs));
@ -72,7 +72,7 @@ test('Schedules job immediately', async () => {
const { schedulerService } = createSchedulerTestService();
const NO_JITTER = 0;
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 10, 'test-id', NO_JITTER);
@ -84,7 +84,7 @@ test('Does not schedule job immediately when paused', async () => {
const { schedulerService, maintenanceService } =
createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await toggleMaintenanceMode(maintenanceService, true);
await schedulerService.schedule(job, 10, 'test-id-2');
@ -96,7 +96,7 @@ test('Does not schedule job immediately when paused', async () => {
test('Can schedule a single regular job', async () => {
const { schedulerService } = createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 50, 'test-id-3');
await ms(75);
@ -109,7 +109,7 @@ test('Scheduled job ignored in a paused mode', async () => {
const { schedulerService, maintenanceService } =
createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await toggleMaintenanceMode(maintenanceService, true);
await schedulerService.schedule(job, 50, 'test-id-4');
@ -123,7 +123,7 @@ test('Can resume paused job', async () => {
const { schedulerService, maintenanceService } =
createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await toggleMaintenanceMode(maintenanceService, true);
await schedulerService.schedule(job, 50, 'test-id-5');
@ -137,8 +137,8 @@ test('Can resume paused job', async () => {
test('Can schedule multiple jobs at the same interval', async () => {
const { schedulerService } = createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const anotherJob = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const anotherJob = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 50, 'test-id-6');
await schedulerService.schedule(anotherJob, 50, 'test-id-7');
@ -152,8 +152,8 @@ test('Can schedule multiple jobs at the same interval', async () => {
test('Can schedule multiple jobs at the different intervals', async () => {
const { schedulerService } = createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const anotherJob = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const anotherJob = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 100, 'test-id-8');
await schedulerService.schedule(anotherJob, 200, 'test-id-9');
@ -242,7 +242,7 @@ it('should emit scheduler job time event when scheduled function is run', async
test('Delays initial job execution by jitter duration', async () => {
const { schedulerService } = createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const jitterMs = 10;
await schedulerService.schedule(job, 10000, 'test-id', jitterMs);
@ -256,7 +256,7 @@ test('Delays initial job execution by jitter duration', async () => {
test('Does not apply jitter if schedule interval is smaller than max jitter', async () => {
const { schedulerService } = createSchedulerTestService();
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
// default jitter 2s-30s
await schedulerService.schedule(job, 1000, 'test-id');
@ -269,7 +269,7 @@ test('Does not allow to run scheduled job when it is already pending', async ()
const { schedulerService } = createSchedulerTestService();
const NO_JITTER = 0;
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const slowJob = async () => {
job();
await ms(25);

View File

@ -28,7 +28,7 @@ import type {
} from '../../openapi/index.js';
import { DEFAULT_ENV, extractAuditInfoFromUser } from '../../util/index.js';
import { DEFAULT_PROJECT, TEST_AUDIT_USER } from '../../types/index.js';
import { beforeAll, afterAll, afterEach, test, describe, expect } from 'vitest';
let db: ITestDb;
let app: IUnleashTest;
@ -55,11 +55,9 @@ const fetchFeatureStrategies = (featureName: string) =>
.expect(200)
.then((res) => res.body);
const fetchClientFeatures = (): Promise<IFeatureToggleClient[]> => {
return app.request
.get(FEATURES_CLIENT_BASE_PATH)
.expect(200)
.then((res) => res.body.features);
const fetchClientFeatures = async (): Promise<IFeatureToggleClient[]> => {
const res = await app.request.get(FEATURES_CLIENT_BASE_PATH).expect(200);
return res.body.features;
};
const createSegment = (postData: UpsertSegmentSchema): Promise<ISegment> => {
@ -235,7 +233,7 @@ test('should validate segment constraint values limit', async () => {
await expect(
createSegment({ name: randomId(), constraints }),
).rejects.toThrow(
).rejects.toThrowError(
`Segments may not have more than ${DEFAULT_SEGMENT_VALUES_LIMIT} values`,
);
});
@ -256,7 +254,7 @@ test('should validate segment constraint values limit with multiple constraints'
await expect(
createSegment({ name: randomId(), constraints }),
).rejects.toThrow(
).rejects.toThrowError(
`Segments may not have more than ${DEFAULT_SEGMENT_VALUES_LIMIT} values`,
);
});
@ -514,7 +512,7 @@ describe('project-specific segments', () => {
...segment,
project: project2,
}),
).rejects.toThrow(
).rejects.toThrowError(
`Invalid project. Segment is being used by strategies in other projects: ${project1}`,
);
});
@ -543,12 +541,12 @@ describe('project-specific segments', () => {
[strategy],
project1,
);
await expect(() =>
await expect(
updateSegment(segment.id, {
...segment,
project: '',
}),
).resolves;
).resolves.toBeUndefined();
});
test(`can't set a specific segment project when being used by multiple projects (global)`, async () => {
@ -589,12 +587,12 @@ describe('project-specific segments', () => {
[strategy2],
project2,
);
await expect(() =>
await expect(
updateSegment(segment.id, {
...segment,
project: project1,
}),
).rejects.toThrow(
).rejects.toThrowError(
`Invalid project. Segment is being used by strategies in other projects: ${project1}, ${project2}`,
);
});

View File

@ -9,7 +9,7 @@ import apiTokenMiddleware, {
} from './api-token-middleware.js';
import type { ApiTokenService } from '../services/index.js';
import type { IUnleashConfig } from '../types/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let config: IUnleashConfig;
@ -24,15 +24,15 @@ beforeEach(() => {
test('should not do anything if request does not contain a authorization', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
getUserForToken: vi.fn(),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn(),
header: vi.fn(),
};
await func(req, undefined, cb);
@ -43,15 +43,15 @@ test('should not do anything if request does not contain a authorization', async
test('should not add user if unknown token', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
getUserForToken: vi.fn(),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-token'),
header: vi.fn().mockReturnValue('some-token'),
user: undefined,
};
@ -64,15 +64,15 @@ test('should not add user if unknown token', async () => {
test('should not make database query when provided PAT format', async () => {
const apiTokenService = {
getUserForToken: jest.fn(),
getUserForToken: vi.fn(),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('user:asdkjsdhg3'),
header: vi.fn().mockReturnValue('user:asdkjsdhg3'),
user: undefined,
};
@ -94,15 +94,15 @@ test('should add user if known token', async () => {
secret: 'a',
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
getUserForToken: vi.fn().mockReturnValue(apiUser),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-known-token'),
header: vi.fn().mockReturnValue('some-known-token'),
user: undefined,
path: '/api/client',
};
@ -127,11 +127,11 @@ test('should not add user if not /api/client', async () => {
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
getUserForToken: vi.fn().mockReturnValue(apiUser),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const res = {
status: (code: unknown) => ({
@ -143,7 +143,7 @@ test('should not add user if not /api/client', async () => {
};
const req = {
header: jest.fn().mockReturnValue('some-known-token'),
header: vi.fn().mockReturnValue('some-known-token'),
user: undefined,
path: '/api/admin',
};
@ -165,7 +165,7 @@ test('should not add user if disabled', async () => {
secret: 'a',
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
getUserForToken: vi.fn().mockReturnValue(apiUser),
} as unknown as ApiTokenService;
const disabledConfig = createTestConfig({
@ -178,14 +178,14 @@ test('should not add user if disabled', async () => {
const func = apiTokenMiddleware(disabledConfig, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-known-token'),
header: vi.fn().mockReturnValue('some-known-token'),
user: undefined,
};
const send = jest.fn();
const send = vi.fn();
const res = {
status: () => {
return {
@ -211,10 +211,10 @@ test('should call next if apiTokenService throws', async () => {
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-token'),
header: vi.fn().mockReturnValue('some-token'),
user: undefined,
};
@ -225,7 +225,7 @@ test('should call next if apiTokenService throws', async () => {
});
test('should call next if apiTokenService throws x2', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn());
const apiTokenService = {
getUserForToken: () => {
throw new Error('hi there, i am stupid');
@ -234,10 +234,10 @@ test('should call next if apiTokenService throws x2', async () => {
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-token'),
header: vi.fn().mockReturnValue('some-token'),
user: undefined,
};
@ -256,15 +256,15 @@ test('should add user if client token and /edge/metrics', async () => {
secret: 'a',
});
const apiTokenService = {
getUserForToken: jest.fn().mockReturnValue(apiUser),
getUserForToken: vi.fn().mockReturnValue(apiUser),
} as unknown as ApiTokenService;
const func = apiTokenMiddleware(config, { apiTokenService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('some-known-token'),
header: vi.fn().mockReturnValue('some-known-token'),
user: undefined,
path: '/edge/metrics',
method: 'POST',

View File

@ -3,14 +3,14 @@ import type { IUnleashConfig } from '../types/index.js';
import { createTestConfig } from '../../test/config/test-config.js';
import getLogger from '../../test/fixtures/no-logger.js';
import type { Request, Response } from 'express';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
const exampleSignalToken = 'signal_tokensecret';
describe('bearerTokenMiddleware', () => {
const req = { headers: {}, path: '' } as Request;
const res = {} as Response;
const next = jest.fn();
const next = vi.fn();
let config: IUnleashConfig;

View File

@ -1,6 +1,6 @@
import type { Request, Response } from 'express';
import requireContentType from './content_type_checker.js';
import { jest } from '@jest/globals';
import { type Mock, vi } from 'vitest';
const mockRequest: (contentType: string) => Request = (contentType) => ({
// @ts-ignore
@ -12,7 +12,7 @@ const mockRequest: (contentType: string) => Request = (contentType) => ({
},
});
const returns415: (t: jest.Mock) => Response = (t) => ({
const returns415: (t: Mock) => Response = (t) => ({
// @ts-ignore
status: (code) => {
expect(415).toBe(code);
@ -25,7 +25,7 @@ const returns415: (t: jest.Mock) => Response = (t) => ({
},
});
const expectNoCall: (t: jest.Mock) => Response = (t) => ({
const expectNoCall: (t: Mock) => Response = (t) => ({
// @ts-ignore
status: () => ({
// @ts-ignore
@ -38,8 +38,8 @@ const expectNoCall: (t: jest.Mock) => Response = (t) => ({
test('Content-type middleware should by default only support application/json', () => {
const middleware = requireContentType();
const t = jest.fn();
const fail = jest.fn();
const t = vi.fn();
const fail = vi.fn();
middleware(mockRequest('application/json'), expectNoCall(fail), t);
middleware(mockRequest('text/plain'), returns415(t), fail);
expect(t).toHaveBeenCalledTimes(2);
@ -48,8 +48,8 @@ test('Content-type middleware should by default only support application/json',
test('Content-type middleware should by default only support application/json with charset', () => {
const middleware = requireContentType();
const t = jest.fn();
const fail = jest.fn();
const t = vi.fn();
const fail = vi.fn();
middleware(
mockRequest('application/json; charset=UTF-8'),
expectNoCall(fail),
@ -62,8 +62,8 @@ test('Content-type middleware should by default only support application/json wi
test('Content-type middleware should allow adding custom supported types', () => {
const middleware = requireContentType('application/yaml');
const t = jest.fn();
const fail = jest.fn();
const t = vi.fn();
const fail = vi.fn();
middleware(mockRequest('application/yaml'), expectNoCall(fail), t);
middleware(mockRequest('text/html'), returns415(t), fail);
middleware(mockRequest('text/plain'), returns415(t), fail);
@ -73,8 +73,8 @@ test('Content-type middleware should allow adding custom supported types', () =>
test('adding custom supported types no longer supports default type', () => {
const middleware = requireContentType('application/yaml');
const t = jest.fn();
const fail = jest.fn();
const t = vi.fn();
const fail = vi.fn();
middleware(mockRequest('application/json'), returns415(t), fail);
expect(t).toHaveBeenCalledTimes(1);
expect(fail).toHaveBeenCalledTimes(0);
@ -86,8 +86,8 @@ test('Should be able to add multiple content-types supported', () => {
'application/yaml',
'form/multipart',
);
const fail = jest.fn();
const succeed = jest.fn();
const fail = vi.fn();
const succeed = vi.fn();
middleware(mockRequest('application/json'), expectNoCall(fail), succeed);
middleware(mockRequest('application/yaml'), expectNoCall(fail), succeed);
middleware(mockRequest('form/multipart'), expectNoCall(fail), succeed);

View File

@ -4,7 +4,7 @@ import { createTestConfig } from '../../test/config/test-config.js';
import type { Request, Response } from 'express';
import { EventEmitter } from 'events';
import { REQUEST_ORIGIN } from '../metric-events.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
const TEST_UNLEASH_TOKEN = 'TEST_UNLEASH_TOKEN';
const TEST_USER_AGENT = 'TEST_USER_AGENT';
@ -12,17 +12,17 @@ const TEST_USER_AGENT = 'TEST_USER_AGENT';
describe('originMiddleware', () => {
const req = { headers: {}, path: '' } as Request;
const res = {} as Response;
const next = jest.fn();
const next = vi.fn();
const loggerMock = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
fatal: vi.fn(),
};
const getLogger = jest.fn(() => loggerMock);
const getLogger = vi.fn(() => loggerMock);
const eventBus = new EventEmitter();
eventBus.emit = jest.fn() as () => boolean;
eventBus.emit = vi.fn() as () => boolean;
let config: IUnleashConfig;

View File

@ -4,7 +4,7 @@ import User from '../types/user.js';
import NotFoundError from '../error/notfound-error.js';
import type { AccountService } from '../services/account-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let config: any;
@ -12,7 +12,7 @@ beforeEach(() => {
config = {
getLogger,
flagResolver: {
isEnabled: jest.fn().mockReturnValue(true),
isEnabled: vi.fn().mockReturnValue(true),
},
};
});
@ -20,16 +20,16 @@ beforeEach(() => {
test('should not set user if unknown token', async () => {
// @ts-expect-error wrong type
const accountService = {
getAccountByPersonalAccessToken: jest.fn(),
addPATSeen: jest.fn(),
getAccountByPersonalAccessToken: vi.fn(),
addPATSeen: vi.fn(),
} as AccountService;
const func = patMiddleware(config, { accountService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('user:some-token'),
header: vi.fn().mockReturnValue('user:some-token'),
user: undefined,
};
@ -43,15 +43,15 @@ test('should not set user if unknown token', async () => {
test('should not set user if token wrong format', async () => {
// @ts-expect-error wrong type
const accountService = {
getAccountByPersonalAccessToken: jest.fn(),
getAccountByPersonalAccessToken: vi.fn(),
} as AccountService;
const func = patMiddleware(config, { accountService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('token-not-starting-with-user'),
header: vi.fn().mockReturnValue('token-not-starting-with-user'),
user: undefined,
};
@ -72,16 +72,16 @@ test('should add user if known token', async () => {
});
// @ts-expect-error wrong type
const accountService = {
getAccountByPersonalAccessToken: jest.fn().mockReturnValue(apiUser),
addPATSeen: jest.fn(),
getAccountByPersonalAccessToken: vi.fn().mockReturnValue(apiUser),
addPATSeen: vi.fn(),
} as AccountService;
const func = patMiddleware(config, { accountService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('user:some-known-token'),
header: vi.fn().mockReturnValue('user:some-known-token'),
user: undefined,
path: '/api/client',
};
@ -104,10 +104,10 @@ test('should call next if accountService throws exception', async () => {
const func = patMiddleware(config, { accountService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('user:some-token'),
header: vi.fn().mockReturnValue('user:some-token'),
user: undefined,
};
@ -121,8 +121,8 @@ test('Should not log at error level if user not found', async () => {
const fakeLogger = {
debug: () => {},
info: () => {},
warn: jest.fn(),
error: jest.fn(),
warn: vi.fn(),
error: vi.fn(),
fatal: console.error,
};
const conf = {
@ -130,20 +130,20 @@ test('Should not log at error level if user not found', async () => {
return fakeLogger;
},
flagResolver: {
isEnabled: jest.fn().mockReturnValue(true),
isEnabled: vi.fn().mockReturnValue(true),
},
};
// @ts-expect-error wrong type
const accountService = {
getAccountByPersonalAccessToken: jest.fn().mockImplementation(() => {
getAccountByPersonalAccessToken: vi.fn().mockImplementation(() => {
throw new NotFoundError('Could not find pat');
}),
} as AccountService;
const mw = patMiddleware(conf, { accountService });
const cb = jest.fn();
const cb = vi.fn();
const req = {
header: jest.fn().mockReturnValue('user:some-token'),
header: vi.fn().mockReturnValue('user:some-token'),
user: undefined,
};

View File

@ -10,7 +10,7 @@ import { ApiTokenType } from '../types/model.js';
import { type ISegmentStore, SYSTEM_USER_ID } from '../types/index.js';
import FakeSegmentStore from '../../test/fixtures/fake-segment-store.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let config: IUnleashConfig;
let featureToggleStore: IFeatureToggleStore;
@ -24,7 +24,7 @@ beforeEach(() => {
test('should add checkRbac to request', () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -33,9 +33,9 @@ test('should add checkRbac to request', () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req = jest.fn();
const req = vi.fn();
func(req, undefined, cb);
@ -47,7 +47,7 @@ test('should add checkRbac to request', () => {
test('should give api-user ADMIN permission', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -56,7 +56,7 @@ test('should give api-user ADMIN permission', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new ApiUser({
tokenName: 'api',
@ -79,7 +79,7 @@ describe('ADMIN tokens should have user id -1337 when only passed through rbac-m
/// Will be -42 (ADMIN_USER.id) when we have the api-token-middleware run first
test('Should give ADMIN api-user userid -1337 (SYSTEM_USER_ID)', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -88,7 +88,7 @@ describe('ADMIN tokens should have user id -1337 when only passed through rbac-m
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new ApiUser({
tokenName: 'api',
@ -107,7 +107,7 @@ describe('ADMIN tokens should have user id -1337 when only passed through rbac-m
/// Will be -42 (ADMIN_USER.id) when we have the api-token-middleware run first
test('Also when checking against permission NONE, userid should still be -1337', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -116,7 +116,7 @@ describe('ADMIN tokens should have user id -1337 when only passed through rbac-m
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new ApiUser({
tokenName: 'api',
@ -136,7 +136,7 @@ describe('ADMIN tokens should have user id -1337 when only passed through rbac-m
test('should not give api-user ADMIN permission', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -145,7 +145,7 @@ test('should not give api-user ADMIN permission', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new ApiUser({
tokenName: 'api',
@ -166,9 +166,9 @@ test('should not give api-user ADMIN permission', async () => {
});
test('should not allow user to miss userId', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn());
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -177,7 +177,7 @@ test('should not allow user to miss userId', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: {
username: 'user',
@ -192,9 +192,9 @@ test('should not allow user to miss userId', async () => {
});
test('should return false for missing user', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn());
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -203,7 +203,7 @@ test('should return false for missing user', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {};
func(req, undefined, cb);
@ -216,7 +216,7 @@ test('should return false for missing user', async () => {
test('should verify permission for root resource', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -225,7 +225,7 @@ test('should verify permission for root resource', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({
username: 'user',
@ -249,7 +249,7 @@ test('should verify permission for root resource', async () => {
test('should lookup projectId from params', async () => {
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -258,7 +258,7 @@ test('should lookup projectId from params', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({
username: 'user',
@ -286,11 +286,10 @@ test('should lookup projectId from feature flag', async () => {
const featureName = 'some-feature-flag';
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
// @ts-expect-error unknown fn type
featureToggleStore.getProjectId = jest.fn().mockReturnValue(projectId);
featureToggleStore.getProjectId = vi.fn().mockReturnValue(projectId);
const func = rbacMiddleware(
config,
@ -298,7 +297,7 @@ test('should lookup projectId from feature flag', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({
username: 'user',
@ -326,7 +325,7 @@ test('should lookup projectId from data', async () => {
const featureName = 'some-feature-flag';
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -335,7 +334,7 @@ test('should lookup projectId from data', async () => {
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({
username: 'user',
@ -364,17 +363,16 @@ test('Does not double check permission if not changing project when updating fla
const oldProjectId = 'some-project-34';
const featureName = 'some-feature-flag';
const accessService = {
hasPermission: jest.fn().mockReturnValue(true),
hasPermission: vi.fn().mockReturnValue(true),
} as PermissionChecker;
// @ts-expect-error unknown fn type
featureToggleStore.getProjectId = jest.fn().mockReturnValue(oldProjectId);
featureToggleStore.getProjectId = vi.fn().mockReturnValue(oldProjectId);
const func = rbacMiddleware(
config,
{ featureToggleStore, segmentStore },
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({ username: 'user', id: 1 }),
params: { featureName },
@ -394,7 +392,7 @@ test('Does not double check permission if not changing project when updating fla
test('CREATE_TAG_TYPE does not need projectId', async () => {
const accessService = {
hasPermission: jest.fn().mockReturnValue(true),
hasPermission: vi.fn().mockReturnValue(true),
} as PermissionChecker;
const func = rbacMiddleware(
@ -402,7 +400,7 @@ test('CREATE_TAG_TYPE does not need projectId', async () => {
{ featureToggleStore, segmentStore },
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({ username: 'user', id: 1 }),
params: {},
@ -422,7 +420,7 @@ test('CREATE_TAG_TYPE does not need projectId', async () => {
test('UPDATE_TAG_TYPE does not need projectId', async () => {
const accessService = {
hasPermission: jest.fn().mockReturnValue(true),
hasPermission: vi.fn().mockReturnValue(true),
} as PermissionChecker;
const func = rbacMiddleware(
@ -430,7 +428,7 @@ test('UPDATE_TAG_TYPE does not need projectId', async () => {
{ featureToggleStore, segmentStore },
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({ username: 'user', id: 1 }),
params: {},
@ -450,7 +448,7 @@ test('UPDATE_TAG_TYPE does not need projectId', async () => {
test('DELETE_TAG_TYPE does not need projectId', async () => {
const accessService = {
hasPermission: jest.fn().mockReturnValue(true),
hasPermission: vi.fn().mockReturnValue(true),
} as PermissionChecker;
const func = rbacMiddleware(
@ -458,7 +456,7 @@ test('DELETE_TAG_TYPE does not need projectId', async () => {
{ featureToggleStore, segmentStore },
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({ username: 'user', id: 1 }),
params: {},
@ -480,7 +478,7 @@ test('should not expect featureName for UPDATE_FEATURE when projectId specified'
const projectId = 'some-project-33';
const accessService = {
hasPermission: jest.fn(),
hasPermission: vi.fn(),
} as PermissionChecker;
const func = rbacMiddleware(
@ -489,7 +487,7 @@ test('should not expect featureName for UPDATE_FEATURE when projectId specified'
accessService,
);
const cb = jest.fn();
const cb = vi.fn();
const req: any = {
user: new User({
username: 'user',

View File

@ -3,7 +3,7 @@ import {
storeRequestedRoute,
} from './response-time-metrics.js';
import { REQUEST_TIME } from '../metric-events.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import type { IFlagResolver } from '../server-impl.js';
import EventEmitter from 'events';
@ -20,10 +20,10 @@ const isDefined = async (timeInfo: any, limit = 10) => {
};
const flagResolver = {
isEnabled: jest.fn(),
getAll: jest.fn(),
getVariant: jest.fn(),
getStaticContext: jest.fn(),
isEnabled: vi.fn(),
getAll: vi.fn(),
getVariant: vi.fn(),
getStaticContext: vi.fn(),
} as IFlagResolver;
// Make sure it's always cleaned up
@ -32,7 +32,7 @@ beforeEach(() => {
res = {
statusCode: 200,
locals: {}, // res will always have locals (according to express RequestHandler type)
once: jest.fn((event: string, callback: () => void) => {
once: vi.fn((event: string, callback: () => void) => {
if (event === 'finish') {
callback();
}
@ -42,7 +42,7 @@ beforeEach(() => {
describe('responseTimeMetrics new behavior', () => {
const instanceStatsService = {
getAppCountSnapshot: jest.fn() as () => number | undefined,
getAppCountSnapshot: vi.fn() as () => number | undefined,
};
const eventBus = new EventEmitter();

View File

@ -134,6 +134,11 @@ describe.each(metaRules)('OpenAPI schemas $name', (rule) => {
expect(validateMetaSchema.errors).toBeNull();
}
});
} else {
// Added, because vitest requires tests for all exceptions.
it(`${schemaName}`, () => {
expect(true).toBe(true);
});
}
});
});

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`updateEnvironmentSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`apiTokenSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`changePasswordSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`clientApplicationSchema no fields 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`clientFeaturesSchema no fields 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`clientMetricsSchema should fail when required field is missing 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`constraintSchema invalid operator name 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`contextFieldSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`deprecatedProjectOverviewSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`emailSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`featureEnvironmentSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`featureSchema constraints 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`featureTypeCountSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`featureTypeSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`meSchema empty 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`projectOverviewSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`roleSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`segmentStrategiesSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`updateEnvironmentSchema 1`] = `undefined`;

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`setStrategySortOrderSchema missing id 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`sortOrderSchema invalid value type 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`strategySchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`tokenUserSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`updateFeatureStrategySegmentsSchema schema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`upsertSegmentSchema 1`] = `
{

View File

@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`validatePasswordSchema empty 1`] = `
{

View File

@ -6,6 +6,7 @@ import {
} from '../../../lib/openapi/spec/playground-request-schema.js';
import { validateSchema } from '../validate.js';
import { generate as generateContext } from './sdk-context-schema.test.js';
import { test } from '@fast-check/vitest';
export const generate = (): Arbitrary<PlaygroundRequestSchema> =>
fc.record({

View File

@ -6,7 +6,7 @@ import {
import { validateSchema } from '../validate.js';
import { generate as generateInput } from './playground-request-schema.test.js';
import { generate as generateFeature } from './playground-feature-schema.test.js';
import { test } from '@fast-check/vitest';
const generate = (): Arbitrary<PlaygroundResponseSchema> =>
fc.record({
input: generateInput(),
@ -15,7 +15,9 @@ const generate = (): Arbitrary<PlaygroundResponseSchema> =>
}),
});
test('playgroundResponseSchema', () =>
test(
'playgroundResponseSchema',
() =>
fc.assert(
fc.property(
generate(),
@ -23,4 +25,6 @@ test('playgroundResponseSchema', () =>
validateSchema(playgroundResponseSchema.$id, data) ===
undefined,
),
));
),
{ timeout: 60000 },
);

View File

@ -5,7 +5,7 @@ import {
sdkContextSchema,
} from './sdk-context-schema.js';
import { commonISOTimestamp } from '../../../test/arbitraries.test.js';
import { test } from '@fast-check/vitest';
export const generate = (): Arbitrary<SdkContextSchema> =>
fc.record(
{

View File

@ -7,7 +7,7 @@ import permissions from '../../../test/fixtures/permissions.js';
import { RoleName, RoleType } from '../../types/model.js';
import type { IUnleashStores } from '../../types/index.js';
import type TestAgent from 'supertest/lib/agent.d.ts';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
describe('Public Signup API', () => {
async function getSetup() {
@ -19,8 +19,8 @@ describe('Public Signup API', () => {
stores.accessStore = {
...stores.accessStore,
addUserToRole: jest.fn() as () => Promise<void>,
removeRolesOfTypeForUser: jest.fn() as () => Promise<void>,
addUserToRole: vi.fn() as () => Promise<void>,
removeRolesOfTypeForUser: vi.fn() as () => Promise<void>,
};
const services = createServices(stores, config);

View File

@ -4,7 +4,7 @@ import createStores from '../../../test/fixtures/store.js';
import permissions from '../../../test/fixtures/permissions.js';
import getApp from '../../app.js';
import { createServices } from '../../services/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
async function getSetup() {
const randomBase = `/random${Math.round(Math.random() * 1000)}`;
@ -113,14 +113,14 @@ test('validate format when updating strategy', async () => {
});
test('editable=false will stop delete request', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn());
const { request, base } = await getSetup();
const name = 'default';
return request.delete(`${base}/api/admin/strategies/${name}`).expect(500);
});
test('editable=false will stop edit request', async () => {
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn());
vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn());
const { request, base } = await getSetup();
const name = 'default';
return request

View File

@ -8,7 +8,7 @@ import SessionService from '../services/session-service.js';
import FakeSessionStore from '../../test/fixtures/fake-session-store.js';
import noLogger from '../../test/fixtures/no-logger.js';
import { addDays } from 'date-fns';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
test('should redirect to "/" after logout', async () => {
const baseUriPath = '';
@ -146,7 +146,7 @@ test('should clear "unleash-session" cookie even when disabled clear site data',
test('should call destroy on session', async () => {
const baseUriPath = '';
const fakeSession = {
destroy: jest.fn(),
destroy: vi.fn(),
};
const app = express();
const config = createTestConfig({ server: { baseUriPath } });
@ -171,7 +171,7 @@ test('should call destroy on session', async () => {
test('should handle req.logout with callback function', async () => {
// passport >=0.6.0
const baseUriPath = '';
const logoutFunction = jest.fn((cb: (err?: any) => void) => cb());
const logoutFunction = vi.fn((cb: (err?: any) => void) => cb());
const app = express();
const config = createTestConfig({ server: { baseUriPath } });
app.use((req: IAuthRequest, res, next) => {
@ -196,7 +196,7 @@ test('should handle req.logout with callback function', async () => {
test('should handle req.logout without callback function', async () => {
// passport <0.6.0
const baseUriPath = '';
const logoutFunction = jest.fn();
const logoutFunction = vi.fn();
const app = express();
const config = createTestConfig({ server: { baseUriPath } });
app.use((req: IAuthRequest, res, next) => {
@ -220,7 +220,7 @@ test('should handle req.logout without callback function', async () => {
test('should redirect to alternative logoutUrl', async () => {
const fakeSession = {
destroy: jest.fn(),
destroy: vi.fn(),
logoutUrl: '/some-other-path',
};
const app = express();
@ -248,7 +248,7 @@ test('Should destroy sessions for user', async () => {
const app = express();
const config = createTestConfig();
const fakeSession = {
destroy: jest.fn(),
destroy: vi.fn(),
user: {
id: 1,
},

View File

@ -7,7 +7,7 @@ import permissions from '../../test/fixtures/permissions.js';
import { RoleName, RoleType } from '../types/model.js';
import type { IUnleashStores } from '../types/index.js';
import type TestAgent from 'supertest/lib/agent.d.ts';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
describe('Public Signup API', () => {
async function getSetup() {
@ -19,8 +19,8 @@ describe('Public Signup API', () => {
stores.accessStore = {
...stores.accessStore,
addUserToRole: jest.fn() as () => Promise<void>,
removeRolesOfTypeForUser: jest.fn() as () => Promise<void>,
addUserToRole: vi.fn() as () => Promise<void>,
removeRolesOfTypeForUser: vi.fn() as () => Promise<void>,
getRolesForUserId: () => Promise.resolve([]),
getRootRoleForUser: () =>
Promise.resolve({

View File

@ -10,25 +10,25 @@ import {
start,
type UnleashFactoryMethods,
} from './server-impl.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
import type MetricsMonitor from './metrics.js';
const mockFactories: () => UnleashFactoryMethods = () => ({
createDb: jest.fn<(config: IUnleashConfig) => Db>().mockReturnValue({
destroy: jest.fn(),
createDb: vi.fn<(config: IUnleashConfig) => Db>().mockReturnValue({
destroy: vi.fn(),
} as unknown as Db),
createStores: jest
createStores: vi
.fn<(config: IUnleashConfig, db: Db) => IUnleashStores>()
.mockReturnValue({
settingStore: {
get: jest.fn(),
postgresVersion: jest.fn(),
get: vi.fn(),
postgresVersion: vi.fn(),
},
eventStore: {
on: jest.fn(),
on: vi.fn(),
},
} as unknown as IUnleashStores),
createServices: jest
createServices: vi
.fn<
(
stores: IUnleashStores,
@ -38,24 +38,24 @@ const mockFactories: () => UnleashFactoryMethods = () => ({
>()
.mockReturnValue({
userService: {
initAdminUser: jest.fn(),
initAdminUser: vi.fn(),
},
schedulerService: {
schedule: jest.fn(),
stop: jest.fn(),
schedule: vi.fn(),
stop: vi.fn(),
},
addonService: {
destroy: jest.fn(),
destroy: vi.fn(),
},
openApiService: {
// returns a middleware
validPath: jest.fn().mockReturnValue(() => {}),
validPath: vi.fn().mockReturnValue(() => {}),
},
} as unknown as IUnleashServices),
createSessionDb:
jest.fn<(config: IUnleashConfig, db: Db) => RequestHandler>(),
createMetricsMonitor: jest.fn<() => MetricsMonitor>().mockReturnValue({
startMonitoring: jest.fn(),
vi.fn<(config: IUnleashConfig, db: Db) => RequestHandler>(),
createMetricsMonitor: vi.fn<() => MetricsMonitor>().mockReturnValue({
startMonitoring: vi.fn(),
} as unknown as MetricsMonitor),
});

View File

@ -27,6 +27,7 @@ import BadDataError from '../../lib/error/bad-data-error.js';
import { createFakeEventsService } from '../../lib/features/events/createEventsService.js';
import { createFakeAccessReadModel } from '../features/access/createAccessReadModel.js';
import { ROLE_CREATED } from '../events/index.js';
import { expect } from 'vitest';
function getSetup() {
const config = createTestConfig({
@ -56,10 +57,14 @@ test('should fail when name exists', async () => {
SYSTEM_USER_AUDIT,
);
expect(accessService.validateRole(existingRole)).rejects.toThrow(
await expect(() =>
accessService.validateRole(existingRole),
).rejects.toThrowError(
expect.errorWithMessage(
new NameExistsError(
`There already exists a role with the name ${existingRole.name}`,
),
),
);
});
@ -110,7 +115,7 @@ test('should not accept empty names', async () => {
await expect(
accessService.validateRole(withWhitespaceName),
).rejects.toThrow('"name" is not allowed to be empty');
).rejects.toThrowError('"name" is not allowed to be empty');
});
test('should trim leading and trailing whitespace from names', async () => {

View File

@ -14,7 +14,7 @@ import {
API_TOKEN_DELETED,
API_TOKEN_UPDATED,
} from '../events/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
test('Should init api token', async () => {
const token = {
@ -170,7 +170,7 @@ describe('API token getTokenWithCache', () => {
test('should return the token and perform only one db query', async () => {
const { apiTokenService, apiTokenStore } = setup();
const apiTokenStoreGet = jest.spyOn(apiTokenStore, 'get');
const apiTokenStoreGet = vi.spyOn(apiTokenStore, 'get');
// valid token not present in cache (could be inserted by another instance)
apiTokenStore.insert(token);
@ -185,9 +185,9 @@ describe('API token getTokenWithCache', () => {
});
test('should query the db only once for invalid tokens', async () => {
jest.useFakeTimers();
vi.useFakeTimers();
const { apiTokenService, apiTokenStore } = setup();
const apiTokenStoreGet = jest.spyOn(apiTokenStore, 'get');
const apiTokenStoreGet = vi.spyOn(apiTokenStore, 'get');
const invalidToken = 'invalid-token';
for (let i = 0; i < 5; i++) {
@ -198,7 +198,7 @@ describe('API token getTokenWithCache', () => {
expect(apiTokenStoreGet).toHaveBeenCalledTimes(1);
// after more than 5 minutes we should be able to query again
jest.advanceTimersByTime(minutesToMilliseconds(6));
vi.advanceTimersByTime(minutesToMilliseconds(6));
for (let i = 0; i < 5; i++) {
expect(
await apiTokenService.getTokenWithCache(invalidToken),
@ -209,7 +209,7 @@ describe('API token getTokenWithCache', () => {
test('should not return the token if it has expired and shoud perform only one db query', async () => {
const { apiTokenService, apiTokenStore } = setup();
const apiTokenStoreGet = jest.spyOn(apiTokenStore, 'get');
const apiTokenStoreGet = vi.spyOn(apiTokenStore, 'get');
// valid token not present in cache but expired
apiTokenStore.insert({ ...token, expiresAt: subDays(new Date(), 1) });
@ -234,7 +234,7 @@ test('normalizes api token type casing to lowercase', async () => {
sortOrder: 1,
});
const apiTokenStoreInsert = jest.spyOn(apiTokenStore, 'insert');
const apiTokenStoreInsert = vi.spyOn(apiTokenStore, 'insert');
await apiTokenService.createApiTokenWithProjects(
{

View File

@ -1,7 +1,7 @@
import { EmailService, type TransportProvider } from './email-service.js';
import noLoggerProvider from '../../test/fixtures/no-logger.js';
import type { IUnleashConfig } from '../types/index.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
test('Can send reset email', async () => {
const emailService = new EmailService({
@ -54,7 +54,7 @@ test('Can send welcome mail', async () => {
});
test('Can supply additional SMTP transport options', async () => {
const transport = jest.fn() as unknown as TransportProvider;
const transport = vi.fn() as unknown as TransportProvider;
new EmailService(
{

View File

@ -6,7 +6,7 @@ import SettingService from './setting-service.js';
import type EventService from '../features/events/event-service.js';
import MaintenanceService from '../features/maintenance/maintenance-service.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
function ms(timeMs: number) {
return new Promise((resolve) => setTimeout(resolve, timeMs));
@ -51,7 +51,7 @@ beforeEach(() => {
});
test('Schedules job immediately', async () => {
const job = jest.fn() as () => Promise<void> as () => Promise<void>;
const job = vi.fn() as () => Promise<void> as () => Promise<void>;
await schedulerService.schedule(job, 10, 'test-id');
expect(job).toHaveBeenCalledTimes(1);
@ -59,7 +59,7 @@ test('Schedules job immediately', async () => {
});
test('Can schedule a single regular job', async () => {
const job = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 50, 'test-id-3');
await ms(75);
@ -68,8 +68,8 @@ test('Can schedule a single regular job', async () => {
});
test('Can schedule multiple jobs at the same interval', async () => {
const job = jest.fn() as () => Promise<void>;
const anotherJob = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const anotherJob = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 50, 'test-id-6');
await schedulerService.schedule(anotherJob, 50, 'test-id-7');
@ -81,8 +81,8 @@ test('Can schedule multiple jobs at the same interval', async () => {
});
test('Can schedule multiple jobs at the different intervals', async () => {
const job = jest.fn() as () => Promise<void>;
const anotherJob = jest.fn() as () => Promise<void>;
const job = vi.fn() as () => Promise<void>;
const anotherJob = vi.fn() as () => Promise<void>;
await schedulerService.schedule(job, 100, 'test-id-8');
await schedulerService.schedule(anotherJob, 200, 'test-id-9');

View File

@ -1,4 +1,5 @@
import { tagSchema } from './tag-schema.js';
import { test, expect } from 'vitest';
test('should require url friendly type if defined', () => {
const tag = {
@ -8,7 +9,7 @@ test('should require url friendly type if defined', () => {
const { error } = tagSchema.validate(tag);
if (error === undefined) {
fail('Did not receive an expected error');
expect.fail('Did not receive an expected error');
}
expect(error.details[0].message).toEqual('"type" must be URL friendly');
});

View File

@ -15,8 +15,7 @@ import SettingService from './setting-service.js';
import FakeSettingStore from '../../test/fixtures/fake-setting-store.js';
import { extractAuditInfoFromUser } from '../util/index.js';
import { createFakeEventsService } from '../features/index.js';
import { jest } from '@jest/globals';
import { vi, expect, test, describe, beforeEach } from 'vitest';
const config: IUnleashConfig = createTestConfig();
const systemUser = new User({ id: -1, username: 'system' });
@ -76,11 +75,10 @@ describe('Default admin initialization', () => {
const CUSTOM_ADMIN_PASSWORD = 'custom-password';
let userService: UserService;
const sendGettingStartedMailMock =
jest.fn() as () => Promise<IEmailEnvelope>;
const sendGettingStartedMailMock = vi.fn() as () => Promise<IEmailEnvelope>;
beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
const userStore = new UserStoreMock();
const accessService = new AccessServiceMock();
@ -91,7 +89,7 @@ describe('Default admin initialization', () => {
);
const emailService = new EmailService(config);
emailService.configured = jest.fn(() => true);
emailService.configured = vi.fn(() => true);
emailService.sendGettingStartedMail = sendGettingStartedMailMock;
const sessionStore = new FakeSessionStore();
@ -187,7 +185,7 @@ describe('Default admin initialization', () => {
});
test('Should use the correct environment variables when initializing the default admin account', async () => {
jest.resetModules();
vi.resetModules();
process.env.UNLEASH_DEFAULT_ADMIN_USERNAME = CUSTOM_ADMIN_USERNAME;
process.env.UNLEASH_DEFAULT_ADMIN_PASSWORD = CUSTOM_ADMIN_PASSWORD;
@ -465,7 +463,7 @@ test('Should send password reset email if user exists', async () => {
});
const knownUser = service.createResetPasswordEmail('known@example.com');
expect(knownUser).resolves.toBeInstanceOf(URL);
await expect(knownUser).resolves.toBeInstanceOf(URL);
});
test('Should throttle password reset email', async () => {
@ -511,17 +509,17 @@ test('Should throttle password reset email', async () => {
generateImageUrl: () => '',
});
jest.useFakeTimers();
vi.useFakeTimers();
const attempt1 = service.createResetPasswordEmail('known@example.com');
await expect(attempt1).resolves.toBeInstanceOf(URL);
const attempt2 = service.createResetPasswordEmail('known@example.com');
await expect(attempt2).rejects.toThrow(
await expect(attempt2).rejects.toThrowError(
'You can only send one new reset password email per minute, per user. Please try again later.',
);
jest.runAllTimers();
vi.runAllTimers();
const attempt3 = service.createResetPasswordEmail('known@example.com');
await expect(attempt3).resolves.toBeInstanceOf(URL);

View File

@ -1,7 +1,6 @@
import dbInit, { type ITestDb } from '../../test/e2e/helpers/database-init.js';
import { SYSTEM_USER } from './core.js';
import getLogger from '../../test/fixtures/no-logger.js';
import { jest } from '@jest/globals';
describe('System user definitions in code and db', () => {
let dbDefinition: {
@ -13,7 +12,6 @@ describe('System user definitions in code and db', () => {
};
let db: ITestDb;
beforeAll(async () => {
jest.setTimeout(15000);
db = await dbInit('system_user_alignment_test', getLogger);
const query = await db.rawDatabase.raw(

View File

@ -1,16 +0,0 @@
// Partial types for "@unleash/express-openapi".
declare module '@unleash/express-openapi' {
import type { RequestHandler } from 'express';
export interface IExpressOpenApi extends RequestHandler {
validPath: (operation: OpenAPIV3.OperationObject) => RequestHandler;
schema: (name: string, schema: OpenAPIV3.SchemaObject) => void;
swaggerui: RequestHandler;
}
export default function openapi(
docsPath: string,
document: Omit<OpenAPIV3.Document, 'paths'>,
options?: { coerce: boolean; extendRefs: boolean },
): IExpressOpenApi;
}

View File

@ -1,18 +1,18 @@
import { jest } from '@jest/globals';
import { type Mock, vi } from 'vitest';
import { batchExecute } from './batchExecute.js';
jest.useFakeTimers();
vi.useFakeTimers();
describe('batchExecute', () => {
let mockExecuteFn: jest.Mock;
let mockExecuteFn: Mock;
beforeEach(() => {
mockExecuteFn = jest.fn();
mockExecuteFn = vi.fn();
});
afterEach(() => {
jest.clearAllTimers();
jest.clearAllMocks();
vi.clearAllTimers();
vi.clearAllMocks();
});
it('should process each item in batches of the specified size', async () => {
@ -23,7 +23,7 @@ describe('batchExecute', () => {
batchExecute(items, batchSize, delayMs, mockExecuteFn);
for (let i = 0; i < 2; i++) {
jest.advanceTimersByTime(delayMs);
vi.advanceTimersByTime(delayMs);
await Promise.resolve();
}
@ -42,11 +42,11 @@ describe('batchExecute', () => {
expect(mockExecuteFn).toHaveBeenCalledTimes(5);
jest.advanceTimersByTime(delayMs);
vi.advanceTimersByTime(delayMs);
await Promise.resolve();
expect(mockExecuteFn).toHaveBeenCalledTimes(10);
jest.advanceTimersByTime(delayMs);
vi.advanceTimersByTime(delayMs);
await Promise.resolve();
expect(mockExecuteFn).toHaveBeenCalledTimes(15);
});

View File

@ -9,6 +9,7 @@ function registerGracefulShutdown(unleash: IUnleash, logger: Logger): void {
logger.info('Unleash has been successfully stopped.');
process.exit(0);
} catch (e) {
console.log('Exiting with code 1');
logger.error('Unable to shutdown Unleash. Hard exit!');
process.exit(1);
}

View File

@ -3,7 +3,7 @@ import { createTestConfig } from '../../test/config/test-config.js';
import { compareAndLogPostgresVersion } from './postgres-version-checker.js';
import FakeSettingStore from '../../test/fixtures/fake-setting-store.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
let config: IUnleashConfig;
let settingStore: ISettingStore;
@ -12,7 +12,7 @@ let errorMessages: string[];
const fakeSettingStore = (postgresVersion: string): ISettingStore => {
const temp = new FakeSettingStore();
jest.spyOn(temp, 'postgresVersion').mockResolvedValue(postgresVersion);
vi.spyOn(temp, 'postgresVersion').mockResolvedValue(postgresVersion);
return temp;
};

View File

@ -1,5 +1,5 @@
import timer from './timer.js';
import { jest } from '@jest/globals';
import { vi } from 'vitest';
function timeout(fn, ms): Promise<void> {
return new Promise((resolve) =>
@ -18,14 +18,14 @@ test('should calculate the correct time in seconds', () => {
});
test('timer should track the time', async () => {
jest.useFakeTimers();
vi.useFakeTimers();
const tt = timer.new();
let diff: number | undefined;
timeout(() => {
diff = tt();
}, 20);
jest.advanceTimersByTime(20);
vi.advanceTimersByTime(20);
expect(diff).toBeGreaterThan(0.0019);
expect(diff).toBeLessThan(0.05);
jest.useRealTimers();
vi.useRealTimers();
});

View File

@ -28,7 +28,6 @@ const initializeTemplateDb = (db: IUnleashConfig['db']): Promise<void> => {
);
await Promise.all(
result.rows.map((row) => {
console.log(`Dropping test database ${row.datname}`);
return client.query(`DROP DATABASE ${row.datname}`);
}),
);

Some files were not shown because too many files have changed in this diff Show More