diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index ad461b01414..2fc94f0b491 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -52,6 +52,7 @@ - xo-server minor - xo-server-audit patch - xo-server-netbox patch +- xo-server-test patch - xo-web minor diff --git a/packages/xo-server-test/.babelrc.js b/packages/xo-server-test/.babelrc.js deleted file mode 100644 index 64023da1ca5..00000000000 --- a/packages/xo-server-test/.babelrc.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -const pkg = require('./package.json') - -// `xo-server-test` is a special package which has no dev dependencies but our -// babel config generator only looks in `devDependencies`. -require('assert').strictEqual(pkg.devDependencies, undefined) -pkg.devDependencies = pkg.dependencies - -module.exports = require('../../@xen-orchestra/babel-config')(pkg) diff --git a/packages/xo-server-test/.eslintrc.js b/packages/xo-server-test/.eslintrc.js deleted file mode 120000 index fdeb2788a6d..00000000000 --- a/packages/xo-server-test/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -../../scripts/babel-eslintrc.js \ No newline at end of file diff --git a/packages/xo-server-test/src/_config.js b/packages/xo-server-test/_config.mjs similarity index 60% rename from packages/xo-server-test/src/_config.js rename to packages/xo-server-test/_config.mjs index c254594b37c..a1dcbd22a34 100644 --- a/packages/xo-server-test/src/_config.js +++ b/packages/xo-server-test/_config.mjs @@ -1,13 +1,12 @@ import appConf from 'app-conf' import path from 'path' - -/* eslint-env jest */ +import { before } from 'node:test' let config export { config as default } -beforeAll(async () => { +before(async () => { config = await appConf.load('xo-server-test', { - appDir: path.join(__dirname, '..'), + appDir: path.join(process.cwd(), '..'), }) }) diff --git a/packages/xo-server-test/src/_defaultValues.js b/packages/xo-server-test/_defaultValues.mjs similarity index 100% rename from packages/xo-server-test/src/_defaultValues.js rename to packages/xo-server-test/_defaultValues.mjs diff --git a/packages/xo-server-test/src/_randomId.js b/packages/xo-server-test/_randomId.mjs similarity index 100% rename from packages/xo-server-test/src/_randomId.js rename to packages/xo-server-test/_randomId.mjs diff --git a/packages/xo-server-test/src/_xoConnection.js b/packages/xo-server-test/_xoConnection.mjs similarity index 90% rename from packages/xo-server-test/src/_xoConnection.js rename to packages/xo-server-test/_xoConnection.mjs index d58f7eeff19..e5801209400 100644 --- a/packages/xo-server-test/src/_xoConnection.js +++ b/packages/xo-server-test/_xoConnection.mjs @@ -1,16 +1,21 @@ /* eslint-env jest */ import Xo from 'xo-lib' import { Collection as XoCollection } from 'xo-collection' -import { decorateWith } from '@vates/decorate-with' -import { defaultsDeep, find, forOwn, iteratee, pick } from 'lodash' +import { decorateClass } from '@vates/decorate-with' +import defaultsDeep from 'lodash/defaultsDeep.js' +import find from 'lodash/find.js' +import forOwn from 'lodash/forOwn.js' +import iteratee from 'lodash/iteratee.js' +import pick from 'lodash/pick.js' import { defer } from 'golike-defer' import { fromEvent } from 'promise-toolbox' -import { parseDuration } from '@vates/parse-duration' +// import { parseDuration } from '@vates/parse-duration' +// import {before, after, afterEach} from 'node:test' -import config from './_config' -import { getDefaultName } from './_defaultValues' +import config from './_config.mjs' +import { getDefaultName } from './_defaultValues.mjs' -class XoConnection extends Xo { +export class XoConnection extends Xo.default { constructor(opts) { super(opts) @@ -69,7 +74,6 @@ class XoConnection extends Xo { return this.waitObject(id) } - @decorateWith(defer) async connect($defer, credentials = pick(config.xoConnection, 'email', 'password')) { await this.open() $defer.onFailure(() => this.close()) @@ -108,6 +112,7 @@ class XoConnection extends Xo { } async createTempResourceSet(params) { + const xo = this // getConnection(); const { id } = await xo.call('resourceSet.create', params) this._tempResourceDisposers.push('resourceSet.delete', { id }) return id @@ -221,6 +226,7 @@ class XoConnection extends Xo { } async runBackupJob(jobId, scheduleId, { remotes, nExecutions = 1 }) { + const xo = this // getConnection(); for (let i = 0; i < nExecutions; i++) { await xo.call('backupNg.runJob', { id: jobId, schedule: scheduleId }) } @@ -287,23 +293,27 @@ const getConnection = credentials => { return xo.connect(credentials) } -let xo -beforeAll(async () => { +/* +before(async () => { + console.log('before xoconnection') + xo = new XoConnection({ url: config.xoConnection.url }) + console.log('set') // TOFIX: stop tests if the connection is not established properly and show the error - xo = await getConnection() + await getConnection() + console.log('connected') }) -afterAll(async () => { +after(async () => { + console.log('after xoconnection') await xo.deleteDurableResources() await xo.close() - xo = null }) afterEach(async () => { + console.log('afterEach xoconnection') jest.setTimeout(parseDuration(config.deleteTempResourcesTimeout)) await xo.deleteTempResources() }) - -export { xo as default } +*/ export const testConnection = ({ credentials }) => getConnection(credentials).then(connection => connection.close()) @@ -312,3 +322,7 @@ export const testWithOtherConnection = defer(async ($defer, credentials, functio $defer(() => xoUser.close()) await functionToExecute(xoUser) }) + +decorateClass(XoConnection, { + connect: defer, +}) diff --git a/packages/xo-server-test/connect.mjs b/packages/xo-server-test/connect.mjs new file mode 100644 index 00000000000..958b55603f6 --- /dev/null +++ b/packages/xo-server-test/connect.mjs @@ -0,0 +1,20 @@ +import Xo from 'xo-lib' +const XoConnection = Xo.default + +const SERVER_URL = process.env.SERVER_URL || 'http://127.0.0.1:80' + +/** + * @param params Contains {url, email, password} as the optionnal server url, and the email/password of the user + * @return Xo A Xo object connection + **/ +export async function connect({ url = SERVER_URL, email, password }) { + const xo = new XoConnection({ url }) + await xo.open() + try { + await xo.signIn({ email, password }) + } catch (err) { + xo.close() + throw err + } + return xo +} diff --git a/packages/xo-server-test/index.js b/packages/xo-server-test/index.js new file mode 100644 index 00000000000..e3bca98d9ca --- /dev/null +++ b/packages/xo-server-test/index.js @@ -0,0 +1,2 @@ +// @TODO +// Index.js will be the main entry point as defined in package.json diff --git a/packages/xo-server-test/src/backupNg/__snapshots__/backupNg.spec.js.snap b/packages/xo-server-test/old-tests/backupNg/__snapshots__/backupNg.spec.js.snap similarity index 100% rename from packages/xo-server-test/src/backupNg/__snapshots__/backupNg.spec.js.snap rename to packages/xo-server-test/old-tests/backupNg/__snapshots__/backupNg.spec.js.snap diff --git a/packages/xo-server-test/src/backupNg/backupNg.spec.js b/packages/xo-server-test/old-tests/backupNg/backupNg.test.mjs similarity index 98% rename from packages/xo-server-test/src/backupNg/backupNg.spec.js rename to packages/xo-server-test/old-tests/backupNg/backupNg.test.mjs index 4e33cb759f7..09aa2ec07d2 100644 --- a/packages/xo-server-test/src/backupNg/backupNg.spec.js +++ b/packages/xo-server-test/old-tests/backupNg/backupNg.test.mjs @@ -1,12 +1,15 @@ -/* eslint-env jest */ +// 'use strict' +/* import forOwn from 'lodash/forOwn.js' -import { noSuchObject } from 'xo-common/api-errors' +import { noSuchObject } from 'xo-common/api-errors.js' -import config from '../_config' -import randomId from '../_randomId' -import xo from '../_xoConnection' -import { getDefaultName, getDefaultSchedule } from '../_defaultValues' +import config from '../_config.js' +import randomId from '../_randomId.js' +import xo from '../_xoConnection.js' +import { getDefaultName, getDefaultSchedule } from '../_defaultValues.js' + +import assert from 'node:assert' const validateBackupJob = (jobInput, jobOutput, createdSchedule) => { const expectedObj = { @@ -24,7 +27,7 @@ const validateBackupJob = (jobInput, jobOutput, createdSchedule) => { const schedules = jobInput.schedules if (schedules !== undefined) { const scheduleTmpId = Object.keys(schedules)[0] - expect(createdSchedule).toEqual({ + assert.deepEqual(createdSchedule, { ...schedules[scheduleTmpId], enabled: false, id: expect.any(String), @@ -567,3 +570,4 @@ describe('backupNg', () => { }) }, 200e3) }) +*/ diff --git a/packages/xo-server-test/src/old-tests/disk.spec.js b/packages/xo-server-test/old-tests/disk.spec.mjs similarity index 98% rename from packages/xo-server-test/src/old-tests/disk.spec.js rename to packages/xo-server-test/old-tests/disk.spec.mjs index 194f34845ea..76eb96ea6a7 100644 --- a/packages/xo-server-test/src/old-tests/disk.spec.js +++ b/packages/xo-server-test/old-tests/disk.spec.mjs @@ -1,12 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' -import { getConfig, getMainConnection, getSrId, waitObjectState } from './util' +import { getConfig, getMainConnection, getSrId, waitObjectState } from '../util.mjs' import map from 'lodash/map.js' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/docker.spec.js b/packages/xo-server-test/old-tests/docker.spec.mjs similarity index 100% rename from packages/xo-server-test/src/old-tests/docker.spec.js rename to packages/xo-server-test/old-tests/docker.spec.mjs diff --git a/packages/xo-server-test/src/old-tests/group.spec.js b/packages/xo-server-test/old-tests/group.spec.mjs similarity index 99% rename from packages/xo-server-test/src/old-tests/group.spec.js rename to packages/xo-server-test/old-tests/group.spec.mjs index 4c2d5bbd949..39354a8b66e 100644 --- a/packages/xo-server-test/src/old-tests/group.spec.js +++ b/packages/xo-server-test/old-tests/group.spec.mjs @@ -2,6 +2,7 @@ import { find, map } from 'lodash' +// eslint-disable-next-line n/no-missing-import import { createUser, deleteUsers, getUser, xo } from './util.js' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/host.spec.js b/packages/xo-server-test/old-tests/host.spec.mjs similarity index 98% rename from packages/xo-server-test/src/old-tests/host.spec.js rename to packages/xo-server-test/old-tests/host.spec.mjs index 4c6a6243df7..fb84565cd8d 100644 --- a/packages/xo-server-test/src/old-tests/host.spec.js +++ b/packages/xo-server-test/old-tests/host.spec.mjs @@ -2,8 +2,10 @@ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { getAllHosts, getConfig, getMainConnection, getVmToMigrateId, waitObjectState } from './util' import { find, forEach } from 'lodash' diff --git a/packages/xo-server-test/src/issues/index.spec.js b/packages/xo-server-test/old-tests/issues/index.spec.mjs similarity index 97% rename from packages/xo-server-test/src/issues/index.spec.js rename to packages/xo-server-test/old-tests/issues/index.spec.mjs index a2bf2fa91af..1f5c80b0cda 100644 --- a/packages/xo-server-test/src/issues/index.spec.js +++ b/packages/xo-server-test/old-tests/issues/index.spec.mjs @@ -2,8 +2,8 @@ import { parseDuration } from '@vates/parse-duration' -import config from '../_config' -import xo from '../_xoConnection' +import config from '../../_config.mjs' +import xo from '../../_xoConnection.mjs' describe('issue', () => { test('4507', async () => { diff --git a/packages/xo-server-test/src/job/__snapshots__/job.spec.js.snap b/packages/xo-server-test/old-tests/job/__snapshots__/job.spec.js.snap similarity index 100% rename from packages/xo-server-test/src/job/__snapshots__/job.spec.js.snap rename to packages/xo-server-test/old-tests/job/__snapshots__/job.spec.js.snap diff --git a/packages/xo-server-test/src/job/job.spec.js b/packages/xo-server-test/old-tests/job/job.spec.mjs similarity index 87% rename from packages/xo-server-test/src/job/job.spec.js rename to packages/xo-server-test/old-tests/job/job.spec.mjs index 102fae810b3..beb1410a01c 100644 --- a/packages/xo-server-test/src/job/job.spec.js +++ b/packages/xo-server-test/old-tests/job/job.spec.mjs @@ -1,9 +1,10 @@ /* eslint-env jest */ -import { difference, keyBy } from 'lodash' +import config from '../../_config.mjs' +import { describe, it, afterEach } from 'node:test' +import { XoConnection as xo, testWithOtherConnection } from '../../_xoConnection.mjs' -import config from '../_config' -import xo, { testWithOtherConnection } from '../_xoConnection' +const { difference, keyBy } = 'lodash' const ADMIN_USER = { email: 'admin2@admin.net', @@ -14,33 +15,33 @@ const ADMIN_USER = { describe('job', () => { let defaultJob - beforeAll(() => { - defaultJob = { - name: 'jobTest', - timeout: 2000, - type: 'call', - key: 'snapshot', - method: 'vm.snapshot', - paramsVector: { - type: 'crossProduct', - items: [ - { - type: 'set', - values: [ - { - id: config.vms.default, - name: 'test-snapshot', - }, - ], - }, - ], - }, - } - }) + // beforeAll(() => { + // defaultJob = { + // name: 'jobTest', + // timeout: 2000, + // type: 'call', + // key: 'snapshot', + // method: 'vm.snapshot', + // paramsVector: { + // type: 'crossProduct', + // items: [ + // { + // type: 'set', + // values: [ + // { + // id: config.vms.default, + // name: 'test-snapshot', + // }, + // ], + // }, + // ], + // }, + // } + // }) describe('.create() :', () => { it('creates a new job', async () => { - jest.setTimeout(6e3) + // jest.setTimeout(6e3) const userId = await xo.createTempUser(ADMIN_USER) const { email, password } = ADMIN_USER await testWithOtherConnection({ email, password }, async xo => { @@ -201,7 +202,7 @@ describe('job', () => { }) it('runs a job', async () => { - jest.setTimeout(7e4) + // jest.setTimeout(7e4) await xo.createTempServer(config.servers.default) const jobId = await xo.createTempJob(defaultJob) const snapshots = xo.objects.all[config.vms.default].snapshots diff --git a/packages/xo-server-test/src/old-tests/pool.spec.js b/packages/xo-server-test/old-tests/pool.spec.mjs similarity index 95% rename from packages/xo-server-test/src/old-tests/pool.spec.js rename to packages/xo-server-test/old-tests/pool.spec.mjs index a222b190319..dc0d931795a 100644 --- a/packages/xo-server-test/src/old-tests/pool.spec.js +++ b/packages/xo-server-test/old-tests/pool.spec.mjs @@ -1,11 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { getConfig, getMainConnection, waitObjectState } from './util' import find from 'lodash/find.js' diff --git a/packages/xo-server-test/src/old-tests/role.spec.js b/packages/xo-server-test/old-tests/role.spec.mjs similarity index 94% rename from packages/xo-server-test/src/old-tests/role.spec.js rename to packages/xo-server-test/old-tests/role.spec.mjs index 0cc6daf081e..874ed7deeb6 100644 --- a/packages/xo-server-test/src/old-tests/role.spec.js +++ b/packages/xo-server-test/old-tests/role.spec.mjs @@ -1,5 +1,6 @@ /* eslint-env jest */ +// eslint-disable-next-line n/no-missing-import import { xo } from './util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/schedule.spec.js b/packages/xo-server-test/old-tests/schedule.spec.mjs similarity index 97% rename from packages/xo-server-test/src/old-tests/schedule.spec.js rename to packages/xo-server-test/old-tests/schedule.spec.mjs index 6855b29cb94..85116ca8b11 100644 --- a/packages/xo-server-test/src/old-tests/schedule.spec.js +++ b/packages/xo-server-test/old-tests/schedule.spec.mjs @@ -1,11 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { getConfig, getMainConnection, getSchedule, jobTest, scheduleTest } from './util' import map from 'lodash/map.js' diff --git a/packages/xo-server-test/src/old-tests/scheduler.spec.js b/packages/xo-server-test/old-tests/scheduler.spec.mjs similarity index 96% rename from packages/xo-server-test/src/old-tests/scheduler.spec.js rename to packages/xo-server-test/old-tests/scheduler.spec.mjs index 17ab91c9ae7..c7357c14eb4 100644 --- a/packages/xo-server-test/src/old-tests/scheduler.spec.js +++ b/packages/xo-server-test/old-tests/scheduler.spec.mjs @@ -1,11 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { jobTest, scheduleTest, getConfig, getMainConnection, getSchedule } from './util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/server.spec.js b/packages/xo-server-test/old-tests/server.spec.mjs similarity index 99% rename from packages/xo-server-test/src/old-tests/server.spec.js rename to packages/xo-server-test/old-tests/server.spec.mjs index e3675c41aa7..c72121c11c0 100644 --- a/packages/xo-server-test/src/old-tests/server.spec.js +++ b/packages/xo-server-test/old-tests/server.spec.mjs @@ -2,6 +2,7 @@ import { find, map } from 'lodash' +// eslint-disable-next-line n/no-missing-import import { config, rejectionOf, xo } from './util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/token.spec.js b/packages/xo-server-test/old-tests/token.spec.mjs similarity index 99% rename from packages/xo-server-test/src/old-tests/token.spec.js rename to packages/xo-server-test/old-tests/token.spec.mjs index 2f12e667cec..0671597b8c4 100644 --- a/packages/xo-server-test/src/old-tests/token.spec.js +++ b/packages/xo-server-test/old-tests/token.spec.mjs @@ -3,7 +3,7 @@ import { defer } from 'golike-defer' import map from 'lodash/map.js' -import { getConnection, rejectionOf, testConnection, xo } from './util.js' +import { getConnection, rejectionOf, testConnection, xo } from '../util.mjs' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/vbd.spec.js b/packages/xo-server-test/old-tests/vbd.spec.mjs similarity index 97% rename from packages/xo-server-test/src/old-tests/vbd.spec.js rename to packages/xo-server-test/old-tests/vbd.spec.mjs index 005129ba5c8..6a943cbaa82 100644 --- a/packages/xo-server-test/src/old-tests/vbd.spec.js +++ b/packages/xo-server-test/old-tests/vbd.spec.mjs @@ -1,11 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { getConfig, getMainConnection, getVmXoTestPvId, getOneHost, waitObjectState } from './util' import map from 'lodash/map.js' diff --git a/packages/xo-server-test/src/old-tests/vif.spec.js b/packages/xo-server-test/old-tests/vif.spec.mjs similarity index 97% rename from packages/xo-server-test/src/old-tests/vif.spec.js rename to packages/xo-server-test/old-tests/vif.spec.mjs index 4f233f24f92..c36ce9ef99b 100644 --- a/packages/xo-server-test/src/old-tests/vif.spec.js +++ b/packages/xo-server-test/old-tests/vif.spec.mjs @@ -1,11 +1,13 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== import fromEvent from 'promise-toolbox/fromEvent' +// eslint-disable-next-line n/no-missing-import import { getConfig, getMainConnection, getNetworkId, waitObjectState, getVmXoTestPvId } from './util' import map from 'lodash/map.js' diff --git a/packages/xo-server-test/src/old-tests/vm.spec.js b/packages/xo-server-test/old-tests/vm.spec.mjs similarity index 99% rename from packages/xo-server-test/src/old-tests/vm.spec.js rename to packages/xo-server-test/old-tests/vm.spec.mjs index 207d8d21e52..d2c0e69fd8b 100644 --- a/packages/xo-server-test/src/old-tests/vm.spec.js +++ b/packages/xo-server-test/old-tests/vm.spec.mjs @@ -1,6 +1,7 @@ /* eslint-env jest */ // Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must +// eslint-disable-next-line n/no-missing-import import expect from 'must' // =================================================================== @@ -17,6 +18,7 @@ import { getVmToMigrateId, getVmXoTestPvId, waitObjectState, + // eslint-disable-next-line n/no-missing-import } from './util' import { map, find } from 'lodash' diff --git a/packages/xo-server-test/src/old-tests/vm/cd.spec.js b/packages/xo-server-test/old-tests/vm/cd.spec.mjs similarity index 98% rename from packages/xo-server-test/src/old-tests/vm/cd.spec.js rename to packages/xo-server-test/old-tests/vm/cd.spec.mjs index a0e422357a1..43c01bed282 100644 --- a/packages/xo-server-test/src/old-tests/vm/cd.spec.js +++ b/packages/xo-server-test/old-tests/vm/cd.spec.mjs @@ -1,5 +1,6 @@ /* eslint-env jest */ +// eslint-disable-next-line n/no-missing-import import { config, getOrWaitCdVbdPosition, rejectionOf, waitObjectState, xo } from './../util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/vm/life-cyle.spec.js b/packages/xo-server-test/old-tests/vm/life-cyle.spec.mjs similarity index 99% rename from packages/xo-server-test/src/old-tests/vm/life-cyle.spec.js rename to packages/xo-server-test/old-tests/vm/life-cyle.spec.mjs index b73b1e3faed..fc27627e260 100644 --- a/packages/xo-server-test/src/old-tests/vm/life-cyle.spec.js +++ b/packages/xo-server-test/old-tests/vm/life-cyle.spec.mjs @@ -2,6 +2,7 @@ import { map, size } from 'lodash' +// eslint-disable-next-line n/no-missing-import import { config, rejectionOf, waitObjectState, xo } from './../util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/vm/pci.spec.js b/packages/xo-server-test/old-tests/vm/pci.spec.mjs similarity index 96% rename from packages/xo-server-test/src/old-tests/vm/pci.spec.js rename to packages/xo-server-test/old-tests/vm/pci.spec.mjs index 0c201306c8c..4562eafa869 100644 --- a/packages/xo-server-test/src/old-tests/vm/pci.spec.js +++ b/packages/xo-server-test/old-tests/vm/pci.spec.mjs @@ -1,5 +1,6 @@ /* eslint-env jest */ +// eslint-disable-next-line n/no-missing-import import { config, waitObjectState, xo } from './../util' // =================================================================== diff --git a/packages/xo-server-test/src/old-tests/vm/snapshotting.spec.js b/packages/xo-server-test/old-tests/vm/snapshotting.spec.mjs similarity index 98% rename from packages/xo-server-test/src/old-tests/vm/snapshotting.spec.js rename to packages/xo-server-test/old-tests/vm/snapshotting.spec.mjs index 31e9ddc7207..ecdd1cf428b 100644 --- a/packages/xo-server-test/src/old-tests/vm/snapshotting.spec.js +++ b/packages/xo-server-test/old-tests/vm/snapshotting.spec.mjs @@ -2,6 +2,7 @@ import { map, size } from 'lodash' +// eslint-disable-next-line n/no-missing-import import { almostEqual, config, waitObjectState, xo } from './../util' // =================================================================== diff --git a/packages/xo-server-test/package.json b/packages/xo-server-test/package.json index efbd96990fa..3d1a7b7a1c7 100644 --- a/packages/xo-server-test/package.json +++ b/packages/xo-server-test/package.json @@ -2,6 +2,7 @@ "private": true, "name": "xo-server-test", "version": "0.0.0", + "type": "module", "license": "AGPL-3.0-or-later", "description": "Test client for Xo-Server", "homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-test", @@ -16,9 +17,9 @@ "url": "https://vates.fr" }, "preferGlobal": false, - "main": "dist/", + "main": "./", "engines": { - "node": ">=6" + "node": ">=14" }, "dependencies": { "@babel/cli": "^7.1.5", @@ -38,8 +39,8 @@ "xo-lib": "^0.11.1" }, "scripts": { - "watch": "jest --bail --watch", - "start": "jest" + "watch": "node --test --watch", + "start": "node --test" }, "jest": { "modulePathIgnorePatterns": [ diff --git a/packages/xo-server-test/src/user/user.spec.js b/packages/xo-server-test/src/user/user.spec.js deleted file mode 100644 index fd364058b26..00000000000 --- a/packages/xo-server-test/src/user/user.spec.js +++ /dev/null @@ -1,255 +0,0 @@ -/* eslint-env jest */ - -import { forOwn, keyBy } from 'lodash' - -import xo, { testConnection, testWithOtherConnection } from '../_xoConnection' - -const SIMPLE_USER = { - email: 'wayne3@vates.fr', - password: 'batman', -} - -const ADMIN_USER = { - email: 'admin2@admin.net', - password: 'admin', - permission: 'admin', -} - -const withData = (data, fn) => - forOwn(data, (data, title) => { - it(title, () => fn(data)) - }) - -describe('user', () => { - describe('.create() :', () => { - withData( - { - 'creates a user without permission': { - email: 'wayne1@vates.fr', - password: 'batman1', - }, - 'creates a user with permission': { - email: 'wayne2@vates.fr', - password: 'batman2', - permission: 'user', - }, - }, - async data => { - jest.setTimeout(6e3) - const userId = await xo.createTempUser(data) - expect(typeof userId).toBe('string') - expect(await xo.getUser(userId)).toMatchSnapshot({ - id: expect.any(String), - }) - await testConnection({ - credentials: { - email: data.email, - password: data.password, - }, - }) - } - ) - - withData( - { - 'fails trying to create a user without email': { password: 'batman' }, - 'fails trying to create a user without password': { - email: 'wayne@vates.fr', - }, - }, - async data => { - await expect(xo.createTempUser(data)).rejects.toMatchSnapshot() - } - ) - - it('fails trying to create a user with an email already used', async () => { - await xo.createTempUser(SIMPLE_USER) - await expect(xo.createTempUser(SIMPLE_USER)).rejects.toMatchSnapshot() - }) - }) - - describe('.changePassword() :', () => { - it('changes the actual user password', async () => { - jest.setTimeout(7e3) - const user = { - email: 'wayne7@vates.fr', - password: 'batman', - } - const newPassword = 'newpwd' - - await xo.createTempUser(user) - await testWithOtherConnection(user, xo => - expect( - xo.call('user.changePassword', { - oldPassword: user.password, - newPassword, - }) - ).resolves.toMatchSnapshot() - ) - - await testConnection({ - credentials: { - email: user.email, - password: newPassword, - }, - }) - - await expect( - testConnection({ - credentials: user, - }) - ).rejects.toMatchSnapshot() - }) - - it('fails trying to change the password with invalid oldPassword', async () => { - await xo.createTempUser(SIMPLE_USER) - await testWithOtherConnection(SIMPLE_USER, xo => - expect( - xo.call('user.changePassword', { - oldPassword: 'falsepwd', - newPassword: 'newpwd', - }) - ).rejects.toMatchSnapshot() - ) - }) - }) - - describe('.getAll() :', () => { - it('gets all the users created', async () => { - const userId1 = await xo.createTempUser({ - email: 'wayne4@vates.fr', - password: 'batman', - permission: 'user', - }) - const userId2 = await xo.createTempUser({ - email: 'wayne5@vates.fr', - password: 'batman', - permission: 'user', - }) - let users = await xo.call('user.getAll') - expect(Array.isArray(users)).toBe(true) - users = keyBy(users, 'id') - expect(users[userId1]).toMatchSnapshot({ id: expect.any(String) }) - expect(users[userId2]).toMatchSnapshot({ id: expect.any(String) }) - }) - }) - - describe('.set() :', () => { - withData( - { - 'sets an email': { email: 'wayne_modified@vates.fr' }, - 'sets a password': { password: 'newPassword' }, - 'sets a permission': { permission: 'user' }, - 'sets a preference': { - preferences: { - filters: { - VM: { - test: 'name_label: test', - }, - }, - }, - }, - }, - async data => { - jest.setTimeout(6e3) - data.id = await xo.createTempUser(SIMPLE_USER) - expect(await xo.call('user.set', data)).toBe(true) - expect(await xo.getUser(data.id)).toMatchSnapshot({ - id: expect.any(String), - }) - - await testConnection({ - credentials: { - email: data.email === undefined ? SIMPLE_USER.email : data.email, - password: data.password === undefined ? SIMPLE_USER.password : data.password, - }, - }) - } - ) - - withData( - { - 'fails trying to set an email with a non admin user connection': { - email: 'wayne_modified@vates.fr', - }, - 'fails trying to set a password with a non admin user connection': { - password: 'newPassword', - }, - 'fails trying to set a permission with a non admin user connection': { - permission: 'user', - }, - }, - async data => { - data.id = await xo.createTempUser({ - email: 'wayne8@vates.fr', - password: 'batman8', - }) - await xo.createTempUser(SIMPLE_USER) - - await testWithOtherConnection(SIMPLE_USER, xo => expect(xo.call('user.set', data)).rejects.toMatchSnapshot()) - } - ) - - withData( - { - 'fails trying to set its own permission as a non admin user': SIMPLE_USER, - 'fails trying to set its own permission as an admin': { - email: 'admin2@admin.net', - password: 'batman', - permission: 'admin', - }, - }, - async data => { - const id = await xo.createTempUser(data) - const { email, password } = data - await testWithOtherConnection({ email, password }, xo => - expect(xo.call('user.set', { id, permission: 'user' })).rejects.toMatchSnapshot() - ) - } - ) - - it('fails trying to set a property of a nonexistant user', async () => { - await expect( - xo.call('user.set', { - id: 'non-existent-id', - password: SIMPLE_USER.password, - }) - ).rejects.toMatchSnapshot() - }) - - it.skip('fails trying to set an email already used', async () => { - await xo.createTempUser(SIMPLE_USER) - const userId2 = await xo.createTempUser({ - email: 'wayne6@vates.fr', - password: 'batman', - }) - - await expect( - xo.call('user.set', { - id: userId2, - email: SIMPLE_USER.email, - }) - ).rejects.toMatchSnapshot() - }) - }) - - describe('.delete() :', () => { - it('deletes a user successfully with id', async () => { - const userId = await xo.call('user.create', SIMPLE_USER) - expect(await xo.call('user.delete', { id: userId })).toBe(true) - expect(await xo.getUser(userId)).toBe(undefined) - }) - - it('fails trying to delete a user with a nonexistent user', async () => { - await expect(xo.call('user.delete', { id: 'nonexistentId' })).rejects.toMatchSnapshot() - }) - - it('fails trying to delete itself', async () => { - const id = await xo.createTempUser(ADMIN_USER) - const { email, password } = ADMIN_USER - await testWithOtherConnection({ email, password }, xo => - expect(xo.call('user.delete', { id })).rejects.toMatchSnapshot() - ) - }) - }) -}) diff --git a/packages/xo-server-test/src/user/__snapshots__/user.spec.js.snap b/packages/xo-server-test/user/__snapshots__/user.spec.js.snap similarity index 100% rename from packages/xo-server-test/src/user/__snapshots__/user.spec.js.snap rename to packages/xo-server-test/user/__snapshots__/user.spec.js.snap diff --git a/packages/xo-server-test/user/user.test.mjs b/packages/xo-server-test/user/user.test.mjs new file mode 100644 index 00000000000..4bbc3161a21 --- /dev/null +++ b/packages/xo-server-test/user/user.test.mjs @@ -0,0 +1,339 @@ +import { connect } from '../connect.mjs' +import keyBy from 'lodash/keyBy.js' +import assert from 'node:assert' +import { describe, test, before, after } from 'node:test' +import { getUser } from '../util.mjs' + +const SIMPLE_USER = { + email: 'simple_user@vates.fr', + password: 'robin', +} + +const ADMIN_USER = { + email: 'admin2@admin.net', + password: 'admin', + permission: 'admin', +} + +const ERROR_INVALID_CREDENTIALS = /JsonRpcError: invalid credentials/ +const ERROR_USER_CANNOT_DELETE_ITSELF = /JsonRpcError: a user cannot delete itself/ +const ERROR_DELETE_NO_SUCH_USER = /JsonRpcError: no such user nonexistent Id/ +const ERROR_SET_NO_SUCH_USER = /JsonRpcError: no such user non-existent-id/ +const ERROR_INVALID_PARAMETERS = /JsonRpcError: invalid parameters/ +const ERROR_PROPERTY_CAN_ONLY_BE_CHANGED_BY_ADMIN = /JsonRpcError: this properties can only changed by an administrator/ +const ERROR_USER_CANNOT_CHANGE_ITS_OWN_PERMISSION = /JsonRpcError: a user cannot change its own permission/ +const ERROR_USER_ALREADY_EXISTS = /JsonRpcError: the user .* already exists/ +// eslint-disable-next-line no-unused-vars +const ERROR_TOO_FAST_AUTHENTIFICATION_TRIES = /Error: too fast authentication tries/ + +describe(`user tests on`, () => { + let sharedXo + const cleanupTest = [] + before(async () => { + sharedXo = await connect({ + email: 'admin@admin.net', + password: 'admin', + }) + }) + after(async () => { + for (const { method, params } of cleanupTest) { + try { + await sharedXo.call(method, params) + } catch (err) { + console.error('during cleanup', err) + } + } + await sharedXo?.close() + }) + + describe('create/change', () => { + test('user.create', async t => { + const data = { + 'User without permission': { + email: 'wayne1@vates.fr', + password: 'batman1', + permission: 'none', + }, + 'User with permission': { + email: 'wayne2@vates.fr', + password: 'batman2', + permission: 'user', + }, + } + for (const [title, testData] of Object.entries(data)) { + await t.test(title, async () => { + const userId = await sharedXo.call('user.create', testData) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + assert.ok(userId.length === 36) + assert.strictEqual(typeof userId, 'string') + const { email, permission, ...others } = (await sharedXo.call('user.getAll')).find(({ id }) => id === userId) + assert.strictEqual(email, testData.email) + + const AUTHORIZED_PROPERTIES = ['authProviders', 'id', 'email', 'groups', 'permission', 'preferences'] + assert.strictEqual( + Object.keys(others).filter(property => !AUTHORIZED_PROPERTIES.includes(property)).length, + 0, + 'user api must not leak user secrets' + ) + assert.deepEqual(permission, testData.permission) + + await assert.doesNotReject(async () => { + const userXo = await connect({ + email: testData.email, + password: testData.password, + }) + await userXo.close() + }) + }) + } + + await t.test('fails without email', async () => { + const data = { password: 'batman' } + await assert.rejects(async () => { + const userId = await sharedXo.call('user.create', data) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + }, ERROR_INVALID_PARAMETERS) + }) + await t.test('fails without password', async () => { + const data = { email: 'batman@example.com' } + await assert.rejects(async () => { + const userId = await sharedXo.call('user.create', data) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + }, ERROR_INVALID_PARAMETERS) + }) + }) + + test('changes the actual user password', async t => { + const user = { + email: 'wayne7@vates.fr', + password: 'batman', + } + const newPassword = 'newpwd' + + const userId = await sharedXo.call('user.create', user) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + + await t.test('initial connection ok', async () => { + await assert.doesNotReject(async () => { + const userXo = await connect({ + email: user.email, + password: user.password, + }) + await userXo.close() + }) + }) + + const userXo = await connect({ + email: user.email, + password: user.password, + }) + + await t.test("can't change if initial password is invalid", async () => { + await assert.rejects( + userXo.call('user.changePassword', { + oldPassword: 'WRONG PASSWORD', + newPassword, + }), + ERROR_INVALID_CREDENTIALS + ) + }) + await t.test('change password ok', async () => { + await assert.doesNotReject(async () => { + await userXo.call('user.changePassword', { + oldPassword: user.password, + newPassword, + }) + }) + }) + + await t.test('connection with new password ok ', async () => { + await assert.doesNotReject(async () => { + const userXo = await connect({ + email: user.email, + password: newPassword, + }) + await userXo.close() + }) + }) + + await t.test('connection with old password forbidden ', async () => { + await assert.rejects( + connect({ + email: user.email, + password: user.password, + }), + 'JsonRpcError: invalid credentials' + ) + }) + }) + + test('.getAll', async t => { + const NB = 10 + for (let i = 0; i < NB; i++) { + const userId = await sharedXo.call('user.create', { email: `${i}@example.com`, password: 'PWD' }) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + } + + await t.test('get all the created user', async t => { + let users = await sharedXo.call('user.getAll') + users = keyBy(users, 'email') + for (let i = 0; i < NB; i++) { + assert.notStrictEqual(users[`${i}@example.com`], undefined) + } + }) + }) + + test('.set', async t => { + let email = `testset@example.com` + let password = 'PWD' + const userId = await sharedXo.call('user.create', { email, password }) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + + let data = { + 'sets an email': { email: 'wayne_modified@vates.fr' }, + 'sets a password': { password: 'newPassword' }, + 'sets a permission': { permission: 'user' }, + 'sets a preference': { + preferences: { + filters: { + VM: { + test: 'name_label: test', + }, + }, + }, + }, + } + for (const [title, testData] of Object.entries(data)) { + await t.test(title, async t => { + await sharedXo.call('user.set', { ...testData, id: userId }) + const updatedUser = (await sharedXo.call('user.getAll')).find(({ id }) => id === userId) + for (const [key, value] of Object.entries(testData)) { + if (key === 'email') { + email = value + } else if (key === 'password') { + password = value + } else { + assert.deepStrictEqual(updatedUser[key], value) + } + + // prevents ERROR_TOO_FAST_AUTHENTIFICATION_TRIES + await new Promise(resolve => setTimeout(resolve, 2_000)) + + await assert.doesNotReject( + connect({ + email, + password, + }) + ) + } + }) + } + + data = { + 'fails trying to set an email with a non admin user connection': { + email: 'wayne_modified@vates.fr', + }, + 'fails trying to set a password with a non admin user connection': { + password: 'newPassword', + }, + 'fails trying to set a permission with a non admin user connection': { + permission: 'user', + }, + } + const nonAdminUserId = await sharedXo.call('user.create', SIMPLE_USER) + const nonAdminUserXo = await connect(SIMPLE_USER) + for (const [title, testData] of Object.entries(data)) { + await t.test(title, async () => { + await assert.rejects( + nonAdminUserXo.call('user.set', { ...testData, id: userId }), + ERROR_PROPERTY_CAN_ONLY_BE_CHANGED_BY_ADMIN + ) + }) + } + await nonAdminUserXo.close() + await sharedXo.call('user.delete', { id: nonAdminUserId }) + + data = { + 'fails trying to set its own permission as a non admin user': { + email: 'user2@user.net', + password: 'robin', + }, + 'fails trying to set its own permission as an admin': { + email: 'admin2@admin.net', + password: 'batman', + permission: 'admin', + }, + } + for (const [title, testData] of Object.entries(data)) { + await t.test(title, async () => { + const userId = await sharedXo.call('user.create', testData) + + const { email, password } = testData + const xo = await connect({ email, password }) + await assert.rejects( + xo.call('user.set', { id: userId, permission: 'user' }), + email.includes('admin') + ? ERROR_USER_CANNOT_CHANGE_ITS_OWN_PERMISSION + : ERROR_PROPERTY_CAN_ONLY_BE_CHANGED_BY_ADMIN + ) + + await sharedXo.call('user.delete', { id: userId }) + }) + } + + await t.test('fails trying to set a property of a nonexistant user', async () => { + await assert.rejects( + sharedXo.call('user.set', { + id: 'non-existent-id', + password: SIMPLE_USER.password, + }), + ERROR_SET_NO_SUCH_USER + ) + }) + + await t.test('fails trying to set an email already used', async () => { + const userId = await sharedXo.call('user.create', SIMPLE_USER) + const userId2 = await sharedXo.call('user.create', { + email: 'wayne6@vates.fr', + password: 'batman', + }) + + await assert + .rejects( + sharedXo.call('user.set', { + id: userId2, + email: SIMPLE_USER.email, + }), + ERROR_USER_ALREADY_EXISTS + ) + .finally(async () => { + await sharedXo.call('user.delete', { id: userId }) + await sharedXo.call('user.delete', { id: userId2 }) + }) + }) + }) + }) + + describe('.delete() :', () => { + test('deletes a user successfully with id', async () => { + const userId = await sharedXo.call('user.create', SIMPLE_USER) + assert.notEqual(await getUser(sharedXo, userId), undefined) + + assert.equal(await sharedXo.call('user.delete', { id: userId }), true) + assert.equal(await getUser(sharedXo, userId), undefined) + }) + + test('fails trying to delete a user with a nonexistent user', async () => { + await assert.rejects(sharedXo.call('user.delete', { id: 'nonexistent Id' }), ERROR_DELETE_NO_SUCH_USER) + }) + + test('fails trying to delete itself', async () => { + const userId = await sharedXo.call('user.create', ADMIN_USER) + cleanupTest.push({ method: 'user.delete', params: { id: userId } }) + assert.notEqual(await getUser(sharedXo, userId), undefined) + + const xo = await connect(ADMIN_USER) + await assert.rejects(xo.call('user.delete', { id: userId }), ERROR_USER_CANNOT_DELETE_ITSELF) + }) + }) +}) diff --git a/packages/xo-server-test/src/util.js b/packages/xo-server-test/util.mjs similarity index 93% rename from packages/xo-server-test/src/util.js rename to packages/xo-server-test/util.mjs index 1bf14761551..6f825a57d7a 100644 --- a/packages/xo-server-test/src/util.js +++ b/packages/xo-server-test/util.mjs @@ -1,7 +1,10 @@ -import expect from 'must' -import { find, forEach, map, cloneDeep } from 'lodash' +import config from './_config.mjs' -import config from './_config' +import assert from 'node:assert' +import cloneDeep from 'lodash/cloneDeep.js' +import find from 'lodash/find.js' +import forEach from 'lodash/forEach.js' +import map from 'lodash/map.js' export const rejectionOf = promise => promise.then( @@ -141,5 +144,5 @@ export function almostEqual(actual, expected, ignoredAttributes) { deepDelete(actual, ignoredAttribute.split('.')) deepDelete(expected, ignoredAttribute.split('.')) }) - expect(actual).to.be.eql(expected) + assert.equal(actual, expected) }