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:
parent
4d1b44818f
commit
b681702b77
13
.github/workflows/build.yaml
vendored
13
.github/workflows/build.yaml
vendored
@ -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
|
||||
|
27
.github/workflows/build_prs_jest_report.yaml
vendored
27
.github/workflows/build_prs_jest_report.yaml
vendored
@ -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
1
.gitignore
vendored
@ -107,3 +107,4 @@ website/.yarn/*
|
||||
!website/.yarn/sdks
|
||||
!website/.yarn/versions
|
||||
|
||||
coverage/.tmp
|
||||
|
1728
coverage/clover.xml
1728
coverage/clover.xml
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
3781
coverage/lcov.info
3781
coverage/lcov.info
File diff suppressed because it is too large
Load Diff
67141
coverage/report.json
67141
coverage/report.json
File diff suppressed because it is too large
Load Diff
82
package.json
82
package.json
@ -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": {
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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"}"`;
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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"}]}]}"`;
|
||||
|
@ -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"}]}]}"`;
|
||||
|
@ -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)*"
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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', () => {
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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`,
|
||||
),
|
||||
|
@ -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),
|
||||
);
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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 () => {
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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 () => {
|
||||
|
@ -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']);
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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 });
|
||||
|
@ -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,
|
||||
|
@ -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}`),
|
||||
);
|
||||
});
|
||||
|
@ -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'/);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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}`,
|
||||
);
|
||||
});
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`updateEnvironmentSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`apiTokenSchema empty 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`changePasswordSchema empty 1`] = `
|
||||
{
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`contextFieldSchema empty 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`deprecatedProjectOverviewSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`emailSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`featureEnvironmentSchema empty 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`featureSchema constraints 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`featureTypeCountSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`featureTypeSchema empty 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`meSchema empty 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`projectOverviewSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`roleSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`segmentStrategiesSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`updateEnvironmentSchema 1`] = `undefined`;
|
||||
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -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`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`strategySchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`tokenUserSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`updateFeatureStrategySegmentsSchema schema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`upsertSegmentSchema 1`] = `
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`validatePasswordSchema empty 1`] = `
|
||||
{
|
||||
|
@ -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({
|
||||
|
@ -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 },
|
||||
);
|
||||
|
@ -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(
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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({
|
||||
|
@ -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),
|
||||
});
|
||||
|
||||
|
@ -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 () => {
|
||||
|
@ -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(
|
||||
{
|
||||
|
@ -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(
|
||||
{
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
16
src/lib/types/openapi.d.ts
vendored
16
src/lib/types/openapi.d.ts
vendored
@ -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;
|
||||
}
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user