import apps from '../../apps.js'; import appstore from '../../appstore.js'; import backupSites from '../../backupsites.js'; import logger from '../../logger.js'; import constants from '../../constants.js'; import database from '../../database.js'; import assert from 'node:assert/strict'; import mailer from '../../mailer.js'; import nock from 'nock'; import oidcClients from '../../oidcclients.js'; import oidcServer from '../../oidcserver.js'; import server from '../../server.js'; import settings from '../../settings.js'; import superagent from '@cloudron/superagent'; import tasks from '../../tasks.js'; import timers from 'timers/promises'; import tokens from '../../tokens.js'; const { log, trace } = logger('test/common'); const manifest = { 'id': 'io.cloudron.test', 'author': 'The Presidents Of the United States Of America', 'title': 'test title', 'description': 'test description', 'tagline': 'test rocks', 'website': 'http://test.cloudron.io', 'contactEmail': 'test@cloudron.io', 'version': '0.1.0', 'manifestVersion': 2, 'dockerImage': 'cloudron/test:25.2.0', 'healthCheckPath': '/', 'httpPort': 7777, 'tcpPorts': { 'ECHO_SERVER_PORT': { 'title': 'Echo Server Port', 'description': 'Echo server', 'containerPort': 7778 } }, 'addons': { 'oauth': { }, 'redis': { }, 'mysql': { }, 'postgresql': { } } }; const mockApiServerOrigin = 'http://localhost:6060'; const dashboardDomain = 'test.example.com'; const appstoreToken = 'toktok'; const owner = { id: null, username: 'superadmin', password: 'Foobar?1337', email: 'superadmin@cloudron.local', displayName: 'Super Admin', token: null }; const admin = { id: null, username: 'administrator', password: 'Foobar?1339', email: 'admin@cloudron.local', token: null }; const user = { id: null, username: 'user', password: 'Foobar?1338', email: 'user@cloudron.local', token: null }; const app = { id: 'appid', appStoreId: 'appStoreId', installationState: apps.ISTATE_PENDING_INSTALL, runState: 'running', subdomain: 'app', domain: 'test.example.com', fqdn: 'app.test.example.com', manifest, containerId: 'someid', portBindings: {}, accessRestriction: null, memoryLimit: 0, mailboxDomain: 'test.example.com', secondaryDomains: [], redirectDomains: [], aliasDomains: [] }; const serverUrl = `http://localhost:${constants.PORT}`; async function setupServer() { log('Setting up server'); await database.initialize(); await database._clear(); await appstore._setApiServerOrigin(mockApiServerOrigin); await oidcServer.stop(); await server.start(); log('Set up server complete'); } async function setup() { log('Setting up'); await setupServer(); // setup let response = await superagent.post(`${serverUrl}/api/v1/provision/setup`) .send({ domainConfig: { provider: 'noop', domain: dashboardDomain, config: {}, tlsConfig: { provider: 'fallback' } } }); assert.deepEqual(response.status, 200); await timers.setTimeout(2000); // create owner const scope1 = nock(await appstore.getApiServerOrigin()) .post('/api/v1/register_cloudron3', (body) => typeof body.domain === 'string' && typeof body.version === 'string') .reply(201, { cloudronId: 'cid', cloudronToken: 'CLOUDRON_TOKEN' }); const scope2 = nock(await appstore.getApiServerOrigin()) .post('/api/v1/subscription3?accessToken=CLOUDRON_TOKEN', (body) => typeof body.state === 'object' && typeof body.state.users.count === 'number') .reply(200, { features: {} }); response = await superagent.post(`${serverUrl}/api/v1/provision/activate`) .query({ setupToken: 'somesetuptoken' }) .send({ username: owner.username, password: owner.password, email: owner.email }) .ok(() => true); assert.deepEqual(response.status, 201); owner.token = response.body.token; owner.id = response.body.userId; assert.ok(scope1.isDone()); scope1.persist(false); assert.ok(scope2.isDone()); scope2.persist(false); // create an admin response = await superagent.post(`${serverUrl}/api/v1/users`) .query({ access_token: owner.token }) .send({ username: admin.username, email: admin.email, password: admin.password, role: 'admin' }); assert.equal(response.status, 201); admin.id = response.body.id; // HACK to get a token for second user (passwords are generated and the user should have gotten a password setup link...) const token1 = await tokens.add({ identifier: admin.id, clientId: oidcClients.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' }); admin.token = token1.accessToken; // create user response = await superagent.post(`${serverUrl}/api/v1/users`) .query({ access_token: owner.token }) .send({ username: user.username, email: user.email, password: user.password }); assert.equal(response.status, 201); user.id = response.body.id; // HACK to get a token for second user (passwords are generated and the user should have gotten a password setup link...) const token2 = await tokens.add({ identifier: user.id, clientId: oidcClients.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' }); user.token = token2.accessToken; // create app object await apps.add(app.id, app.appStoreId, '', app.manifest, app.subdomain, app.domain, app.portBindings, app); await settings._set(settings.APPSTORE_API_TOKEN_KEY, appstoreToken); // appstore token log('Setup complete'); } async function cleanup() { log('Cleaning up'); await server.stop(); await oidcServer.stop(); if (!nock.isActive()) nock.activate(); log('Cleaned up'); } function clearMailQueue() { mailer.clearMailQueue(); } async function checkMails(number) { await timers.setTimeout(1000); assert.equal(mailer._mailQueue.length, number); clearMailQueue(); } async function waitForTask(taskId) { log(`Waiting for task: ${taskId}`); for (let i = 0; i < 30; i++) { const result = await tasks.get(taskId); assert.notEqual(result, null); if (!result.active) { if (result.success) return result; throw new Error(`Task ${taskId} failed: ${result.error.message} - ${result.error.stack}`); } await timers.setTimeout(2000); log(`Waiting for task to ${taskId} finish`); } throw new Error(`Task ${taskId} never finished`); } async function waitForAsyncTask(es) { return new Promise((resolve, reject) => { const messages = []; es.addEventListener('message', function (message) { log(`waitForAsyncTask: ${message.data}`); messages.push(JSON.parse(message.data)); if (messages[messages.length-1].type === 'done') { log('waitForAsyncTask: finished'); es.close(); resolve(messages); } }); es.addEventListener('error', function (error) { log('waitForAsyncTask: errored', error); es.close(); const e = new Error(error.message); e.code = error.code; reject(e); }); }); } async function getDefaultBackupSite() { const result = await backupSites.list(); return result.find(r => r.name === 'Default'); } export default { setup, setupServer, cleanup, clearMailQueue, checkMails, waitForTask, waitForAsyncTask, owner, admin, user, app, getDefaultBackupSite, mockApiServerOrigin, dashboardDomain, dashboardFqdn: 'my.test.example.com', appstoreToken, mailFqdn: 'my.test.example.com', serverUrl, auditSource: { ip: '5.6.7.8' } };