You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			618 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			618 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
| /* eslint-disable no-console */
 | |
| /* eslint-disable import/no-extraneous-dependencies */
 | |
| /* eslint-disable prefer-destructuring */
 | |
| 
 | |
| const { Application } = require('spectron');
 | |
| const path = require('path');
 | |
| const url = require('url');
 | |
| const http = require('http');
 | |
| const fse = require('fs-extra');
 | |
| const { exec } = require('child_process');
 | |
| 
 | |
| const chai = require('chai');
 | |
| const chaiAsPromised = require('chai-as-promised');
 | |
| const CommonPage = require('./page-objects/common.page');
 | |
| const RegistrationPage = require('./page-objects/registration.page');
 | |
| const ConversationPage = require('./page-objects/conversation.page');
 | |
| const SettingsPage = require('./page-objects/settings.page');
 | |
| 
 | |
| chai.should();
 | |
| chai.use(chaiAsPromised);
 | |
| chai.config.includeStack = true;
 | |
| 
 | |
| const STUB_SNODE_SERVER_PORT = 3000;
 | |
| const ENABLE_LOG = false;
 | |
| 
 | |
| module.exports = {
 | |
|   /* **************  USERS  ****************** */
 | |
|   TEST_MNEMONIC1:
 | |
|     'faxed mechanic mocked agony unrest loincloth pencil eccentric boyfriend oasis speedy ribbon faxed',
 | |
|   TEST_PUBKEY1:
 | |
|     '0552b85a43fb992f6bdb122a5a379505a0b99a16f0628ab8840249e2a60e12a413',
 | |
|   TEST_DISPLAY_NAME1: 'integration_tester_1',
 | |
| 
 | |
|   TEST_MNEMONIC2:
 | |
|     'guide inbound jerseys bays nouns basin sulking awkward stockpile ostrich ascend pylons ascend',
 | |
|   TEST_PUBKEY2:
 | |
|     '054e1ca8681082dbd9aad1cf6fc89a32254e15cba50c75b5a73ac10a0b96bcbd2a',
 | |
|   TEST_DISPLAY_NAME2: 'integration_tester_2',
 | |
| 
 | |
|   TEST_MNEMONIC3:
 | |
|     'alpine lukewarm oncoming blender kiwi fuel lobster upkeep vogue simplest gasp fully simplest',
 | |
|   TEST_PUBKEY3:
 | |
|     '05f8662b6e83da5a31007cc3ded44c601f191e07999acb6db2314a896048d9036c',
 | |
|   TEST_DISPLAY_NAME3: 'integration_tester_3',
 | |
| 
 | |
|   /* **************  OPEN GROUPS  ****************** */
 | |
|   VALID_GROUP_URL: 'https://chat.getsession.org',
 | |
|   VALID_GROUP_URL2: 'https://chat-dev.lokinet.org',
 | |
|   VALID_GROUP_NAME: 'Session Public Chat',
 | |
|   VALID_GROUP_NAME2: 'Loki Dev Chat',
 | |
| 
 | |
|   /* **************  CLOSED GROUPS  ****************** */
 | |
|   VALID_CLOSED_GROUP_NAME1: 'Closed Group 1',
 | |
| 
 | |
|   USER_DATA_ROOT_FOLDER: '',
 | |
| 
 | |
|   async timeout(ms) {
 | |
|     return new Promise(resolve => setTimeout(resolve, ms));
 | |
|   },
 | |
| 
 | |
|   async closeToast(app) {
 | |
|     app.client.element(CommonPage.toastCloseButton).click();
 | |
|   },
 | |
| 
 | |
|   // a wrapper to work around electron/spectron bug
 | |
|   async setValueWrapper(app, selector, value) {
 | |
|     // keys, setValue and addValue hang on certain platforms
 | |
| 
 | |
|     if (process.platform === 'darwin') {
 | |
|       await app.client.execute(
 | |
|         (slctr, val) => {
 | |
|           // eslint-disable-next-line no-undef
 | |
|           const iter = document.evaluate(
 | |
|             slctr,
 | |
|             // eslint-disable-next-line no-undef
 | |
|             document,
 | |
|             null,
 | |
|             // eslint-disable-next-line no-undef
 | |
|             XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
 | |
|             null
 | |
|           );
 | |
|           const elem = iter.iterateNext();
 | |
|           if (elem) {
 | |
|             elem.value = val;
 | |
|           } else {
 | |
|             console.error('Cant find', slctr, elem, iter);
 | |
|           }
 | |
|         },
 | |
|         selector,
 | |
|         value
 | |
|       );
 | |
|       // let session js detect the text change
 | |
|       await app.client.element(selector).click();
 | |
|     } else {
 | |
|       // Linux & Windows don't require wrapper
 | |
|       await app.client.element(selector).setValue(value);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   async startApp(environment = 'test-integration-session') {
 | |
|     const env = environment.startsWith('test-integration')
 | |
|       ? 'test-integration'
 | |
|       : environment;
 | |
|     const instance = environment.replace('test-integration-', '');
 | |
| 
 | |
|     const app1 = new Application({
 | |
|       path: path.join(__dirname, '..', 'node_modules', '.bin', 'electron'),
 | |
|       args: ['.'],
 | |
|       env: {
 | |
|         NODE_ENV: env,
 | |
|         NODE_APP_INSTANCE: instance,
 | |
|         USE_STUBBED_NETWORK: true,
 | |
|         ELECTRON_ENABLE_LOGGING: true,
 | |
|         ELECTRON_ENABLE_STACK_DUMPING: true,
 | |
|         ELECTRON_DISABLE_SANDBOX: 1,
 | |
|       },
 | |
|       requireName: 'electronRequire',
 | |
|       // chromeDriverLogPath: '../chromedriverlog.txt',
 | |
|       chromeDriverArgs: [
 | |
|         `remote-debugging-port=${Math.floor(
 | |
|           Math.random() * (9999 - 9000) + 9000
 | |
|         )}`,
 | |
|       ],
 | |
|     });
 | |
| 
 | |
|     chaiAsPromised.transferPromiseness = app1.transferPromiseness;
 | |
| 
 | |
|     await app1.start();
 | |
|     await app1.client.waitUntilWindowLoaded();
 | |
| 
 | |
|     return app1;
 | |
|   },
 | |
| 
 | |
|   async startApp2() {
 | |
|     const app2 = await this.startApp('test-integration-session-2');
 | |
|     return app2;
 | |
|   },
 | |
| 
 | |
|   async stopApp(app1) {
 | |
|     if (app1 && app1.isRunning()) {
 | |
|       await app1.stop();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   async killallElectron() {
 | |
|     // rtharp - my 2nd client on MacOs needs: pkill -f "node_modules/.bin/electron"
 | |
|     // node_modules/electron/dist/electron is node_modules/electron/dist/Electron.app on MacOS
 | |
|     const killStr =
 | |
|       process.platform === 'win32'
 | |
|         ? 'taskkill /im electron.exe /t /f'
 | |
|         : 'pkill -f "node_modules/electron/dist/electron" | pkill -f "node_modules/.bin/electron"';
 | |
|     return new Promise(resolve => {
 | |
|       exec(killStr, (_err, stdout, stderr) => {
 | |
|         resolve({ stdout, stderr });
 | |
|       });
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   async rmFolder(folder) {
 | |
|     await fse.remove(folder);
 | |
|   },
 | |
| 
 | |
|   async startAndAssureCleanedApp2() {
 | |
|     const app2 = await this.startAndAssureCleanedApp(
 | |
|       'test-integration-session-2'
 | |
|     );
 | |
|     return app2;
 | |
|   },
 | |
| 
 | |
|   async startAndAssureCleanedApp(env = 'test-integration-session') {
 | |
|     const userData = path.join(this.USER_DATA_ROOT_FOLDER, `Session-${env}`);
 | |
| 
 | |
|     await this.rmFolder(userData);
 | |
| 
 | |
|     const app1 = await this.startApp(env);
 | |
|     await app1.client.waitForExist(
 | |
|       RegistrationPage.registrationTabSignIn,
 | |
|       4000
 | |
|     );
 | |
| 
 | |
|     return app1;
 | |
|   },
 | |
| 
 | |
|   async startAndStub({
 | |
|     mnemonic,
 | |
|     displayName,
 | |
|     stubSnode = false,
 | |
|     stubOpenGroups = false,
 | |
|     env = 'test-integration-session',
 | |
|   }) {
 | |
|     const app = await this.startAndAssureCleanedApp(env);
 | |
| 
 | |
|     if (stubSnode) {
 | |
|       await this.startStubSnodeServer();
 | |
|       this.stubSnodeCalls(app);
 | |
|     }
 | |
| 
 | |
|     if (stubOpenGroups) {
 | |
|       this.stubOpenGroupsCalls(app);
 | |
|     }
 | |
| 
 | |
|     if (mnemonic && displayName) {
 | |
|       await this.restoreFromMnemonic(app, mnemonic, displayName);
 | |
|       // not sure we need this - rtharp.
 | |
|       await this.timeout(2000);
 | |
|     }
 | |
| 
 | |
|     return app;
 | |
|   },
 | |
| 
 | |
|   async startAndStubN(props, n) {
 | |
|     // Make app with stub as number n
 | |
|     const appN = await this.startAndStub({
 | |
|       env: `test-integration-session-${n}`,
 | |
|       ...props,
 | |
|     });
 | |
| 
 | |
|     return appN;
 | |
|   },
 | |
| 
 | |
|   async restoreFromMnemonic(app, mnemonic, displayName) {
 | |
|     await app.client.element(RegistrationPage.registrationTabSignIn).click();
 | |
|     await app.client.element(RegistrationPage.restoreFromSeedMode).click();
 | |
|     await this.setValueWrapper(
 | |
|       app,
 | |
|       RegistrationPage.recoveryPhraseInput,
 | |
|       mnemonic
 | |
|     );
 | |
| 
 | |
|     await this.setValueWrapper(
 | |
|       app,
 | |
|       RegistrationPage.displayNameInput,
 | |
|       displayName
 | |
|     );
 | |
| 
 | |
|     // await app.client.element(RegistrationPage.continueSessionButton).click();
 | |
|     await app.client.keys('Enter');
 | |
| 
 | |
|     await app.client.waitForExist(
 | |
|       RegistrationPage.conversationListContainer,
 | |
|       4000
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   async startAppsAsFriends() {
 | |
|     const app1Props = {
 | |
|       mnemonic: this.TEST_MNEMONIC1,
 | |
|       displayName: this.TEST_DISPLAY_NAME1,
 | |
|       stubSnode: true,
 | |
|     };
 | |
| 
 | |
|     const app2Props = {
 | |
|       mnemonic: this.TEST_MNEMONIC2,
 | |
|       displayName: this.TEST_DISPLAY_NAME2,
 | |
|       stubSnode: true,
 | |
|     };
 | |
| 
 | |
|     const [app1, app2] = await Promise.all([
 | |
|       this.startAndStub(app1Props),
 | |
|       this.startAndStubN(app2Props, 2),
 | |
|     ]);
 | |
| 
 | |
|     /** add each other as friends */
 | |
|     const textMessage = this.generateSendMessageText();
 | |
| 
 | |
|     await app1.client.element(ConversationPage.contactsButtonSection).click();
 | |
|     await app1.client.element(ConversationPage.addContactButton).click();
 | |
| 
 | |
|     await this.setValueWrapper(
 | |
|       app1,
 | |
|       ConversationPage.sessionIDInput,
 | |
|       this.TEST_PUBKEY2
 | |
|     );
 | |
|     await app1.client.element(ConversationPage.nextButton).click();
 | |
|     await app1.client.waitForExist(
 | |
|       ConversationPage.sendFriendRequestTextarea,
 | |
|       1000
 | |
|     );
 | |
| 
 | |
|     // send a text message to that user (will be a friend request)
 | |
|     await this.setValueWrapper(
 | |
|       app1,
 | |
|       ConversationPage.sendFriendRequestTextarea,
 | |
|       textMessage
 | |
|     );
 | |
|     await app1.client.keys('Enter');
 | |
|     await app1.client.waitForExist(
 | |
|       ConversationPage.existingFriendRequestText(textMessage),
 | |
|       1000
 | |
|     );
 | |
| 
 | |
|     // wait for left notification Friend Request count to go to 1 and click it
 | |
|     await app2.client.waitForExist(
 | |
|       ConversationPage.oneNotificationFriendRequestLeft,
 | |
|       5000
 | |
|     );
 | |
|     await app2.client
 | |
|       .element(ConversationPage.oneNotificationFriendRequestLeft)
 | |
|       .click();
 | |
|     // open the dropdown from the top friend request count
 | |
|     await app2.client.isExisting(
 | |
|       ConversationPage.oneNotificationFriendRequestTop
 | |
|     ).should.eventually.be.true;
 | |
|     await app2.client
 | |
|       .element(ConversationPage.oneNotificationFriendRequestTop)
 | |
|       .click();
 | |
| 
 | |
|     // accept the friend request and validate that on both side the "accepted FR" message is shown
 | |
|     await app2.client
 | |
|       .element(ConversationPage.acceptFriendRequestButton)
 | |
|       .click();
 | |
|     await app2.client.waitForExist(
 | |
|       ConversationPage.acceptedFriendRequestMessage,
 | |
|       1000
 | |
|     );
 | |
|     await app1.client.waitForExist(
 | |
|       ConversationPage.acceptedFriendRequestMessage,
 | |
|       5000
 | |
|     );
 | |
| 
 | |
|     return [app1, app2];
 | |
|   },
 | |
| 
 | |
|   async addFriendToNewClosedGroup(app, app2) {
 | |
|     await this.setValueWrapper(
 | |
|       app,
 | |
|       ConversationPage.closedGroupNameTextarea,
 | |
|       this.VALID_CLOSED_GROUP_NAME1
 | |
|     );
 | |
|     await app.client
 | |
|       .element(ConversationPage.closedGroupNameTextarea)
 | |
|       .getValue()
 | |
|       .should.eventually.equal(this.VALID_CLOSED_GROUP_NAME1);
 | |
| 
 | |
|     await app.client
 | |
|       .element(ConversationPage.createClosedGroupMemberItem)
 | |
|       .isVisible().should.eventually.be.true;
 | |
| 
 | |
|     // select the first friend as a member of the groups being created
 | |
|     await app.client
 | |
|       .element(ConversationPage.createClosedGroupMemberItem)
 | |
|       .click();
 | |
|     await app.client
 | |
|       .element(ConversationPage.createClosedGroupMemberItemSelected)
 | |
|       .isVisible().should.eventually.be.true;
 | |
| 
 | |
|     // trigger the creation of the group
 | |
|     await app.client
 | |
|       .element(ConversationPage.validateCreationClosedGroupButton)
 | |
|       .click();
 | |
| 
 | |
|     await app.client.waitForExist(
 | |
|       ConversationPage.sessionToastGroupCreatedSuccess,
 | |
|       1000
 | |
|     );
 | |
|     await app.client.isExisting(
 | |
|       ConversationPage.headerTitleGroupName(this.VALID_CLOSED_GROUP_NAME1)
 | |
|     ).should.eventually.be.true;
 | |
|     await app.client.element(ConversationPage.headerTitleMembers(2)).isVisible()
 | |
|       .should.eventually.be.true;
 | |
| 
 | |
|     // validate overlay is closed
 | |
|     await app.client
 | |
|       .isExisting(ConversationPage.leftPaneOverlay)
 | |
|       .should.eventually.be.equal(false);
 | |
| 
 | |
|     // move back to the conversation section
 | |
|     await app.client
 | |
|       .element(ConversationPage.conversationButtonSection)
 | |
|       .click();
 | |
| 
 | |
|     // validate open chat has been added
 | |
|     await app.client.isExisting(
 | |
|       ConversationPage.rowOpenGroupConversationName(
 | |
|         this.VALID_CLOSED_GROUP_NAME1
 | |
|       )
 | |
|     ).should.eventually.be.true;
 | |
| 
 | |
|     // next check app2 has been invited and has the group in its conversations
 | |
|     await app2.client.waitForExist(
 | |
|       ConversationPage.rowOpenGroupConversationName(
 | |
|         this.VALID_CLOSED_GROUP_NAME1
 | |
|       ),
 | |
|       6000
 | |
|     );
 | |
|     // open the closed group conversation on app2
 | |
|     await app2.client
 | |
|       .element(ConversationPage.conversationButtonSection)
 | |
|       .click();
 | |
|     await this.timeout(500);
 | |
|     await app2.client
 | |
|       .element(
 | |
|         ConversationPage.rowOpenGroupConversationName(
 | |
|           this.VALID_CLOSED_GROUP_NAME1
 | |
|         )
 | |
|       )
 | |
|       .click();
 | |
|   },
 | |
| 
 | |
|   async linkApp2ToApp(app1, app2) {
 | |
|     // app needs to be logged in as user1 and app2 needs to be logged out
 | |
|     // start the pairing dialog for the first app
 | |
|     await app1.client.element(SettingsPage.settingsButtonSection).click();
 | |
|     await app1.client
 | |
|       .element(SettingsPage.settingsRowWithText('Devices'))
 | |
|       .click();
 | |
| 
 | |
|     await app1.client.isVisible(ConversationPage.noPairedDeviceMessage);
 | |
|     // we should not find the linkDeviceButtonDisabled button (as DISABLED)
 | |
|     await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled)
 | |
|       .should.eventually.be.false;
 | |
|     await app1.client.element(ConversationPage.linkDeviceButton).click();
 | |
| 
 | |
|     // validate device pairing dialog is shown and has a qrcode
 | |
|     await app1.client.isVisible(ConversationPage.devicePairingDialog);
 | |
|     await app1.client.isVisible(ConversationPage.qrImageDiv);
 | |
| 
 | |
|     // next trigger the link request from the app2 with the app1 pubkey
 | |
|     await app2.client.element(RegistrationPage.registrationTabSignIn).click();
 | |
|     await app2.client.element(RegistrationPage.linkDeviceMode).click();
 | |
| 
 | |
|     await this.setValueWrapper(
 | |
|       app2,
 | |
|       RegistrationPage.textareaLinkDevicePubkey,
 | |
|       this.TEST_PUBKEY1
 | |
|     );
 | |
|     await app2.client.element(RegistrationPage.linkDeviceTriggerButton).click();
 | |
|     await app1.client.waitForExist(RegistrationPage.toastWrapper, 7000);
 | |
|     let secretWordsapp1 = await app1.client
 | |
|       .element(RegistrationPage.secretToastDescription)
 | |
|       .getText();
 | |
|     secretWordsapp1 = secretWordsapp1.split(': ')[1];
 | |
| 
 | |
|     await app2.client.waitForExist(RegistrationPage.toastWrapper, 6000);
 | |
|     await app2.client
 | |
|       .element(RegistrationPage.secretToastDescription)
 | |
|       .getText()
 | |
|       .should.eventually.be.equal(secretWordsapp1);
 | |
|     await app1.client.element(ConversationPage.allowPairingButton).click();
 | |
|     await app1.client.element(ConversationPage.okButton).click();
 | |
|     // validate device paired in settings list with correct secrets
 | |
|     await app1.client.waitForExist(
 | |
|       ConversationPage.devicePairedDescription(secretWordsapp1),
 | |
|       2000
 | |
|     );
 | |
| 
 | |
|     await app1.client.isExisting(ConversationPage.unpairDeviceButton).should
 | |
|       .eventually.be.true;
 | |
|     await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled)
 | |
|       .should.eventually.be.true;
 | |
| 
 | |
|     // validate app2 (secondary device) is linked successfully
 | |
|     await app2.client.waitForExist(
 | |
|       RegistrationPage.conversationListContainer,
 | |
|       4000
 | |
|     );
 | |
| 
 | |
|     // validate primary pubkey of app2 is the same that in app1
 | |
|     await app2.webContents
 | |
|       .executeJavaScript("window.storage.get('primaryDevicePubKey')")
 | |
|       .should.eventually.be.equal(this.TEST_PUBKEY1);
 | |
|   },
 | |
| 
 | |
|   async triggerUnlinkApp2FromApp(app1, app2) {
 | |
|     // check app2 is loggedin
 | |
|     await app2.client.isExisting(RegistrationPage.conversationListContainer)
 | |
|       .should.eventually.be.true;
 | |
| 
 | |
|     await app1.client.element(ConversationPage.settingsButtonSection).click();
 | |
|     await app1.client
 | |
|       .element(ConversationPage.settingsRowWithText('Devices'))
 | |
|       .click();
 | |
|     await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled)
 | |
|       .should.eventually.be.true;
 | |
|     // click the unlink button
 | |
|     await app1.client.element(ConversationPage.unpairDeviceButton).click();
 | |
|     await app1.client.element(ConversationPage.validateUnpairDevice).click();
 | |
| 
 | |
|     await app1.client.waitForExist(
 | |
|       ConversationPage.noPairedDeviceMessage,
 | |
|       2000
 | |
|     );
 | |
|     await app1.client.element(ConversationPage.linkDeviceButton).isEnabled()
 | |
|       .should.eventually.be.true;
 | |
| 
 | |
|     // let time to app2 to catch the event and restart dropping its data
 | |
|     await this.timeout(5000);
 | |
| 
 | |
|     // check that the app restarted
 | |
|     // (did not find a better way than checking the app no longer being accessible)
 | |
|     let isApp2Joinable = true;
 | |
|     try {
 | |
|       await app2.client.isExisting(RegistrationPage.registrationTabSignIn)
 | |
|         .should.eventually.be.true;
 | |
|     } catch (err) {
 | |
|       // if we get an error here, it means Spectron is lost.
 | |
|       // this is a good thing because it means app2 restarted
 | |
|       isApp2Joinable = false;
 | |
|     }
 | |
| 
 | |
|     if (isApp2Joinable) {
 | |
|       throw new Error(
 | |
|         'app2 is still joinable so it did not restart, so it did not unlink correctly'
 | |
|       );
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   async sendMessage(app, messageText, fileLocation = undefined) {
 | |
|     await this.setValueWrapper(
 | |
|       app,
 | |
|       ConversationPage.sendMessageTextarea,
 | |
|       messageText
 | |
|     );
 | |
|     await app.client
 | |
|       .element(ConversationPage.sendMessageTextarea)
 | |
|       .getValue()
 | |
|       .should.eventually.equal(messageText);
 | |
| 
 | |
|     // attach a file
 | |
|     if (fileLocation) {
 | |
|       await this.setValueWrapper(
 | |
|         app,
 | |
|         ConversationPage.attachmentInput,
 | |
|         fileLocation
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     // send message
 | |
|     await app.client.element(ConversationPage.sendMessageTextarea).click();
 | |
|     await app.client.keys('Enter');
 | |
|   },
 | |
| 
 | |
|   generateSendMessageText: () =>
 | |
|     `Test message from integration tests ${Date.now()}`,
 | |
| 
 | |
|   stubOpenGroupsCalls: app1 => {
 | |
|     app1.webContents.executeJavaScript(
 | |
|       'window.LokiAppDotNetServerAPI = window.StubAppDotNetAPI;'
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   stubSnodeCalls(app1) {
 | |
|     app1.webContents.executeJavaScript(
 | |
|       'window.LokiMessageAPI = window.StubMessageAPI;'
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   logsContainsString: async (app1, str) => {
 | |
|     const logs = JSON.stringify(await app1.client.getRenderProcessLogs());
 | |
|     return logs.includes(str);
 | |
|   },
 | |
| 
 | |
|   async startStubSnodeServer() {
 | |
|     if (!this.stubSnode) {
 | |
|       this.messages = {};
 | |
|       this.stubSnode = http.createServer((request, response) => {
 | |
|         const { query } = url.parse(request.url, true);
 | |
|         const { pubkey, data, timestamp } = query;
 | |
| 
 | |
|         if (pubkey) {
 | |
|           if (request.method === 'POST') {
 | |
|             if (ENABLE_LOG) {
 | |
|               console.warn('POST', [data, timestamp]);
 | |
|             }
 | |
| 
 | |
|             let ori = this.messages[pubkey];
 | |
|             if (!this.messages[pubkey]) {
 | |
|               ori = [];
 | |
|             }
 | |
| 
 | |
|             this.messages[pubkey] = [...ori, { data, timestamp }];
 | |
| 
 | |
|             response.writeHead(200, { 'Content-Type': 'text/html' });
 | |
|             response.end();
 | |
|           } else {
 | |
|             const retrievedMessages = { messages: this.messages[pubkey] };
 | |
|             if (ENABLE_LOG) {
 | |
|               console.warn('GET', pubkey, retrievedMessages);
 | |
|             }
 | |
|             if (this.messages[pubkey]) {
 | |
|               response.writeHead(200, { 'Content-Type': 'application/json' });
 | |
|               response.write(JSON.stringify(retrievedMessages));
 | |
|               this.messages[pubkey] = [];
 | |
|             }
 | |
|             response.end();
 | |
|           }
 | |
|         }
 | |
|         response.end();
 | |
|       });
 | |
|       this.stubSnode.listen(STUB_SNODE_SERVER_PORT);
 | |
|     } else {
 | |
|       this.messages = {};
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   async stopStubSnodeServer() {
 | |
|     if (this.stubSnode) {
 | |
|       this.stubSnode.close();
 | |
|       this.stubSnode = null;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // async killStubSnodeServer() {
 | |
|   //   return new Promise(resolve => {
 | |
|   //     exec(
 | |
|   //       `lsof -ti:${STUB_SNODE_SERVER_PORT} |xargs kill -9`,
 | |
|   //       (err, stdout, stderr) => {
 | |
|   //         if (err) {
 | |
|   //           resolve({ stdout, stderr });
 | |
|   //         } else {
 | |
|   //           resolve({ stdout, stderr });
 | |
|   //         }
 | |
|   //       }
 | |
|   //     );
 | |
|   //   });
 | |
|   // },
 | |
| };
 |