Source: test/test-data.js

/**
 * @module  test-data
 * @desc    Module for creating, registering/unregistering test users/activities into the leaderboard-api server.
 * @version 1.0.0
 * @author  Essam A. El-Sherif
 */

/* Import node.js core modules */
import http               from 'node:http';
import https              from 'node:https';
import { fileURLToPath }  from 'node:url';
import { dirname, join }  from 'node:path';

/* Import package dependencies */
import dotenv from 'dotenv';

/* Emulate commonJS __filename and __dirname constants */
const __filename = fileURLToPath(import.meta.url);
const __dirname  = dirname(__filename);

/* Configure dotenv path to read the package .env file */
dotenv.config({path: join(__dirname, '../.env')});

/**
 * @class
 * @static
 * @desc Class for creating, registering/unregistering test users/activities into the leaderboard-api server.
 */
export class TestData{

	#prot = process.env.lb_serverProtocol || 'http';
	#host = process.env.lb_serverHost;
	#port = process.env.lb_serverPort;
	#path = process.env.lb_serverPath;

	#userPrefix = 'user@';
	#passwordPrefix = 'password-';
	#activityPrefix = 'activity-';

	#initUser = 'admin';
	#initPassword = 'admin';
	#initActivity = 'activity-a';

	#usersMap = new Map();
	#actsMap = new Map();

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @param    {number} numUsers - Number of users to create.
	 * @param    {number} numActivities - Number of activities to create.
	 * @param    {boolean} init - To create special user/activity.
	 * @desc     Constructs and returns a TestData object.
	 */
	constructor(numUsers = 2, numActivities = 2, init = true){
		this.numUsers = numUsers;
		this.numActivities = numActivities;
		this.init = init;

		this.createTestUsers();
		this.createTestActivities();
	}

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @desc     Create and keep a set of numUsers users.
	 */
	createTestUsers(){

		if(this.init){
			this.#usersMap.set(this.#initUser, {
				username: this.#initUser,
				password: this.#initPassword,
			});
		}

		for(let i = 1; i <= this.numUsers; i++){
			this.#usersMap.set(`${this.#userPrefix}${i}`, {
				username: `${this.#userPrefix}${i}`,
				password: `${this.#passwordPrefix}${i}`,
			});
		}
	}

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @desc     Create and keep a set of numActivities activities.
	 */
	createTestActivities(){

		if(this.init){
			this.#actsMap.set(this.#initActivity, [{
				activity: this.#initActivity,
				username: this.#initUser,
				score: 100,
			}]);
		}

		for(let i = 1; i <= this.numActivities; i++){
			let acts = [];
			for(let j = 1; j <= this.numUsers; j++){
				acts.push({
					activity: `${this.#activityPrefix}${i}`,
					username: `${this.#userPrefix}${j}`,
					score: i * 100 + j * 10,
				});
			}
			this.#actsMap.set(`${this.#activityPrefix}${i}`, acts);
		}

	}

	/**
     * @member   testUsers
	 * @instance
	 * @readonly
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @desc     Get an array of testUsers objects.
	 */
	get testUsers(){
		return [...this.#usersMap.values()];
	}

	/**
     * @member   testActivities
	 * @instance
	 * @readonly
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @desc     Get an array of testActivities objects.
	 */
	get testActivities(){
		return [...this.#actsMap.values()].flat();
	}

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @returns  {Promise} Promise object that will fulfill with an array of test users registered.
	 * @desc     Register the test users into the leaderboard-api server.
	 */
	registerTestUsers(){

		return Promise.all([...this.#usersMap.values()].map(user => new Promise((resolve) => {

			const url = `${this.#prot}://${this.#host}:${this.#port}${this.#path}/auth`;
			const module = this.#prot === 'https' ? https : http;
			const cr = module.request(url, {method: 'POST', rejectUnauthorized: false}, (res) => {

				let body = '';
				res.on('data', (chunk) => {
					body += chunk;
				});

				res.on('end', () => {
					if(res.statusCode === 201){
						user.token = JSON.parse(body).token;
						resolve(user);
					}
					else{
						resolve(null);
					}
				});
			});

			cr.setHeader('Content-Type', 'application/json');
			cr.write(JSON.stringify(user));
			cr.end();
		})));
	}

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @returns  {Promise} Promise object that will fulfill with an array of test users unregistered.
	 * @desc     Unregister the test users into the leaderboard-api server.
	 */
	unregisterTestUsers(){

		return Promise.all([...this.#usersMap.values()].map(user => new Promise((resolve) => {

			const url = `${this.#prot}://${this.#host}:${this.#port}${this.#path}/auth`;
			const module = this.#prot === 'https' ? https : http;
			const cr = module.request(url, {method: 'PATCH', rejectUnauthorized: false}, (res) => {

				res.on('data', () => {
				});

				res.on('end', () => {
					if(res.statusCode === 204){
						resolve(user);
					}
					else{
						resolve(null);
					}
				});
			});

			delete user.token;

			cr.setHeader('Content-Type', 'application/json');
			cr.write(JSON.stringify(user));
			cr.end();
		})));
	}

	/**
     * @method
	 * @instance
	 * @memberof module:leaderboard-api-test-data.TestData
	 * @returns  {Promise} Promise object that will fulfill with an array of test activities registered.
	 * @desc     Register the test activities into the leaderboard-api server.
	 */
	registerTestActivities(){

		return Promise.all([...this.#actsMap.values()].flat().map(act => new Promise((resolve) => {

			const url = `${this.#prot}://${this.#host}:${this.#port}${this.#path}/score`;
			const module = this.#prot === 'https' ? https : http;
			const cr = module.request(url, {method: 'POST', rejectUnauthorized: false}, (res) => {

				let body = '';
				res.on('data', (chunk) => {
					body += chunk;
				});

				res.on('end', () => {
					if(res.statusCode === 201){
						resolve(JSON.parse(body));
					}
					else{
						resolve(null);
					}
				});
			});

			cr.setHeader('Authorization', `Bearer ${this.#usersMap.get(act.username).token}`);
			cr.setHeader('Content-Type', 'application/json');

			cr.write(JSON.stringify({
				username: act.username,
				activity: act.activity,
				score: act.score,
			}));
			cr.end();
		})));
	}
}