Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: do not run Couch container for unit tests #639

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ jobs:
node-version: ${{ matrix.node-version }}
- name: Install pyxform
run: pip install git+https://github.com/medic/[email protected]#egg=pyxform-medic
- name: npm ci and test
run: npm ci && npm test
- run: npm ci
- run: npm run eslint
- run: npm test
- name: Archive Results
uses: actions/upload-artifact@v4
with:
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
"scripts": {
"clean": "rm -rf ./build/",
"eslint": "eslint 'src/**/*.js' test/*.js 'test/**/*.js'",
"docker-start-couchdb": "npm run docker-stop-couchdb && docker run -d -p 6984:5984 --rm --name cht-conf-couchdb couchdb:2.3.1 && sh test/scripts/wait_for_response_code.sh 6984 200 CouchDB",
"docker-stop-couchdb": "docker stop cht-conf-couchdb || true",
"test": "npm run eslint && npm run docker-start-couchdb && npm run clean && mkdir -p build/test && cp -r test/data build/test/data && cd build/test && nyc --reporter=html mocha --forbid-only \"../../test/**/*.spec.js\" --exclude \"../../test/e2e/**/*.spec.js\" && cd ../.. && npm run docker-stop-couchdb",
"test": "npm run clean && mkdir -p build/test && cp -r test/data build/test/data && cd build/test && nyc --reporter=html mocha \"../../test/**/*.spec.js\" --exclude \"../../test/e2e/**/*.spec.js\"",
"test-e2e": "mocha --config test/e2e/.mocharc.js",
"semantic-release": "semantic-release"
},
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/.mocharc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const chaiAsPromised = require('chai-as-promised');
const chai = require('chai');
chai.use(chaiAsPromised);

module.exports = {
allowUncaught: false,
color: true,
Expand Down
47 changes: 28 additions & 19 deletions test/e2e/cht-conf-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,42 @@ const fs = require('fs');
const fse = require('fs-extra');

const log = require('../../src/lib/log');
const { getProjectUrl } = require('./cht-docker-utils');
const { getProjectUrl, DEFAULT_PROJECT_NAME } = require('./cht-docker-utils');

const getProjectDirectory = (projectName) => path.resolve(__dirname, `../../build/${projectName}`);
const getProjectDirectory = (projectName = DEFAULT_PROJECT_NAME) => path.resolve(__dirname, `../../build/${projectName}`);

const runChtConf = (projectName, command) => new Promise((resolve, reject) => {
getProjectUrl(projectName).then(url => {
const projectDirectory = getProjectDirectory(projectName);
const cliPath = path.join(__dirname, '../../src/bin/index.js');
exec(`node ${cliPath} --url=${url} ${command}`, { cwd: projectDirectory }, (error, stdout, stderr) => {
if (!error) {
return resolve(stdout);
}
const runChtConf = async (
command,
{ url, sessionToken, projectName = DEFAULT_PROJECT_NAME } = {},
) => {
const instanceUrl = url || await getProjectUrl(projectName);
const sessionParam = sessionToken ? `--session-token=${sessionToken}` : '';
const projectDirectory = getProjectDirectory(projectName);
const cliPath = path.join(__dirname, '../../src/bin/index.js');
return new Promise((resolve, reject) => {
exec(
`node ${cliPath} --url=${instanceUrl} ${sessionParam} ${command}`,
{ cwd: projectDirectory },
(error, stdout, stderr) => {
if (!error) {
return resolve(stdout);
}

log.error(stderr);
reject(new Error(stdout.toString('utf8')));
});
log.error(stderr);
reject(new Error(stdout.toString('utf8')));
}
);
});
});
};

const cleanupProject = (projectName) => {
const cleanupProject = (projectName = DEFAULT_PROJECT_NAME) => {
const projectDirectory = getProjectDirectory(projectName);
if (fs.existsSync(projectDirectory)) {
fse.removeSync(projectDirectory);
}
};

const initProject = async (projectName) => {
const initProject = async (projectName = DEFAULT_PROJECT_NAME) => {
const projectDirectory = getProjectDirectory(projectName);
cleanupProject(projectName);

Expand All @@ -46,10 +55,10 @@ const initProject = async (projectName) => {
}, null, 4),
);

await runChtConf(projectName, 'initialise-project-layout');
await runChtConf('initialise-project-layout', { projectName });
};

const writeBaseAppSettings = async (projectName, baseSettings) => {
const writeBaseAppSettings = async (baseSettings, projectName = DEFAULT_PROJECT_NAME) => {
const projectDirectory = getProjectDirectory(projectName);

return await fs.promises.writeFile(
Expand All @@ -58,7 +67,7 @@ const writeBaseAppSettings = async (projectName, baseSettings) => {
);
};

const readCompiledAppSettings = async (projectName) => {
const readCompiledAppSettings = async (projectName = DEFAULT_PROJECT_NAME) => {
const projectDirectory = getProjectDirectory(projectName);

return JSON.parse(
Expand Down
22 changes: 8 additions & 14 deletions test/e2e/edit-app-settings.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { expect } = require('chai');
const request = require('request-promise-native');

const { DEFAULT_PROJECT_NAME, getProjectUrl } = require('./cht-docker-utils');
const { getProjectUrl } = require('./cht-docker-utils');
const {
cleanupProject,
initProject,
Expand All @@ -11,19 +11,13 @@ const {
} = require('./cht-conf-utils');

describe('edit-app-settings', () => {
const projectName = DEFAULT_PROJECT_NAME;
const findLanguage = (settingsLanguages, locale) => settingsLanguages.find(language => language.locale === locale);

before(async () => {
await initProject(projectName);
});

after(async () => {
await cleanupProject(projectName);
});
before(initProject);
after(cleanupProject);
jkuester marked this conversation as resolved.
Show resolved Hide resolved

it('disables a language, recompile, and push app settings', async () => {
const url = await getProjectUrl(projectName);
const url = await getProjectUrl();
const baseSettings = await request.get({ url: `${url}/api/v1/settings`, json: true });
expect(findLanguage(baseSettings.languages, 'en').enabled).to.be.true;
expect(findLanguage(baseSettings.languages, 'fr').enabled).to.be.true;
Expand All @@ -39,18 +33,18 @@ describe('edit-app-settings', () => {
});
baseSettings.locale = 'fr';
baseSettings.locale_outgoing = 'fr';
await writeBaseAppSettings(projectName, baseSettings);
await writeBaseAppSettings(baseSettings);

await runChtConf(projectName, 'compile-app-settings');
const compiledSettings = await readCompiledAppSettings(projectName);
await runChtConf('compile-app-settings');
const compiledSettings = await readCompiledAppSettings();
expect(compiledSettings.languages.find(language => language.locale === 'en')).to.deep.equal({
locale: 'en',
enabled: false,
});
expect(compiledSettings.locale).to.equal('fr');
expect(compiledSettings.locale_outgoing).to.equal('fr');

await runChtConf(projectName, 'upload-app-settings');
await runChtConf('upload-app-settings');
const newSettings = await request.get({ url: `${url}/api/v1/settings`, json: true });
expect(newSettings.languages.find(language => language.locale === 'en')).to.deep.equal({
locale: 'en',
Expand Down
97 changes: 97 additions & 0 deletions test/e2e/session-token.spec.js
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently GitHub is not smart enough to realize that this is a modified version of the test/integration/session-token.spec.js file. I am afraid you will have to manually diff this to see what actually changed in the file....

(TLDR is that I was able to remove a lot of code.)

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const { expect } = require('chai');
const rpn = require('request-promise-native');
const fs = require('fs-extra');
const path = require('path');
const PouchDB = require('pouchdb-core');
const { getProjectUrl } = require('./cht-docker-utils');
const {
cleanupProject,
initProject,
getProjectDirectory, runChtConf,
} = require('./cht-conf-utils');

const COUCH_URL_PATTERN = /^(?<prefix>https?:\/\/)(?<user>[^:]+):(?<password>[^@]+)@(?<rootUrl>.*)$/;

const projectPath = getProjectDirectory();

const createProjectPath = async () => {
await initProject();

const docs = [
{ _id: 'one', name: 'Document One' },
{ _id: 'two', name: 'Document Two' },
];

const jsonDocsPath = `${projectPath}/json_docs`;
fs.ensureDirSync(`${projectPath}/json_docs`);

docs.forEach(doc => {
fs.writeJsonSync(path.join(jsonDocsPath, `${doc._id}.doc.json`), doc);
});
};

describe('session-token', () => {
let authenticatedUrl;
let unauthenticatedUrl;
let sessionToken;
const action = 'upload-docs --force';

const initializeDatabase = () => {
PouchDB.plugin(require('pouchdb-adapter-http'));
PouchDB.plugin(require('pouchdb-mapreduce'));
};

const getSessionToken = async (name, password) => {
const options = {
method: 'POST',
uri: `${unauthenticatedUrl}/_session`,
body: {
name,
password,
},
resolveWithFullResponse: true,
json: true
};

try {
const response = await rpn(options);
const setCookieHeader = response.headers['set-cookie'];
// Extract the session token from the set-cookie header
const sessionCookie = setCookieHeader.find(cookie => cookie.startsWith('AuthSession='));
return sessionCookie.split(';')[0].split('=')[1];
} catch (error) {
throw new Error(`Failed to get session token: ${error.message}`);
}
};

before(async () => {
await createProjectPath();
initializeDatabase();
authenticatedUrl = await getProjectUrl();
const { prefix, user, password, rootUrl } = authenticatedUrl.match(COUCH_URL_PATTERN).groups;
unauthenticatedUrl = `${prefix}${rootUrl}`;
sessionToken = await getSessionToken(user, password);
});

after(cleanupProject);

it('should handle authentication with session token', async () => {
const stdout = await runChtConf(action, { url: unauthenticatedUrl, sessionToken });
expect(stdout).to.contain('INFO upload-docs complete.');
});

it('should handle authentication with basic authentication', async () => {
const stdout = await runChtConf(action, { url: authenticatedUrl });
expect(stdout).to.contain('INFO upload-docs complete.');
});

it('should fail with incorrect session token', async () => {
const incorrectToken = 'incorrect-token';
const promiseToExecute = runChtConf(action, { url: unauthenticatedUrl, sessionToken: incorrectToken });
await expect(promiseToExecute)
.to.be.rejected
.and.eventually.have.property('message')
// Bad Request: Malformed AuthSession cookie
.that.contains('INFO Error: Received error code 400');
});
});
5 changes: 2 additions & 3 deletions test/fn/move-contacts.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const fs = require('../../src/lib/sync-fs');
const environment = require('../../src/lib/environment');

const PouchDB = require('pouchdb-core');
PouchDB.plugin(require('pouchdb-adapter-http'));
PouchDB.plugin(require('pouchdb-adapter-memory'));
PouchDB.plugin(require('pouchdb-mapreduce'));

const moveContactsModule = rewire('../../src/fn/move-contacts');
Expand Down Expand Up @@ -50,8 +50,7 @@ describe('move-contacts', () => {
const updateHierarchyRules = contact_types => upsert('settings', { settings: { contact_types } });

beforeEach(async () => {
// using remote couchdb because of https://github.com/pouchdb/pouchdb/issues/8370
pouchDb = new PouchDB(`http://localhost:6984/scenario${scenarioCount++}`);
pouchDb = new PouchDB(`move-contacts-${scenarioCount++}`);

await mockHierarchy(pouchDb, {
district_1: {
Expand Down
Loading