From 36729eaeabda0555598e0da504ceab87e5f6b912 Mon Sep 17 00:00:00 2001 From: Siddharth VP Date: Sun, 22 Sep 2024 23:24:39 +0530 Subject: [PATCH] bot: prevent concurrent logins --- src/bot.ts | 18 ++++++++++++++++++ tests/login.bot.test.js | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/bot.ts b/src/bot.ts index d70205f..a59f28c 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -561,12 +561,30 @@ export class Mwn { /************** CORE FUNCTIONS *******************/ + private loginInProgress: Promise = null; + /** * Executes a Login * @see https://www.mediawiki.org/wiki/API:Login * @returns {Promise} */ async login(loginOptions?: { username?: string; password?: string; apiUrl?: string }): Promise { + // Avoid multiple logins taking place concurrently, for instance when session loss occurs + // in the middle of a batch operation. + if (!this.loginInProgress) { + this.loginInProgress = this.loginInternal(loginOptions); + this.loginInProgress.then(() => { + this.loginInProgress = null; + }); + } + return this.loginInProgress; + } + + private async loginInternal(loginOptions?: { + username?: string; + password?: string; + apiUrl?: string; + }): Promise { this.options = merge(this.options, loginOptions); if (!this.options.username || !this.options.password || !this.options.apiUrl) { return rejectWithError({ diff --git a/tests/login.bot.test.js b/tests/login.bot.test.js index eca553e..63c7606 100644 --- a/tests/login.bot.test.js +++ b/tests/login.bot.test.js @@ -1,6 +1,7 @@ 'use strict'; const { Mwn, expect, verifyTokenAndSiteInfo } = require('./base/test_base'); +const { bot: localBot, sinon } = require('./base/local_wiki'); const testwiki = require('./mocking/loginCredentials.js'); @@ -17,6 +18,20 @@ describe('login', async function () { }); }); + it('avoids concurrent logins', async function () { + const spy = sinon.spy(localBot, 'loginInternal'); + const [mainPage, serverTime] = await Promise.all([localBot.read('Main Page'), localBot.getServerTime()]); + expect(spy).to.have.been.calledOnce; + sinon.restore(); + expect(serverTime).to.be.a('string'); + expect(mainPage).to.be.a('object'); + + await localBot.logout(); + expect(localBot.loggedIn).to.be.false; + await localBot.login(); + expect(localBot.loggedIn).to.be.true; + }); + let bot = new Mwn(); it('successfully logs in through init', async function () { bot = await Mwn.init(testwiki.account1);