diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index 8dd64ef253..08f6614bad 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -177,7 +177,6 @@ describe('Connections manager', () => { const spyOnDestroyHiddenService = jest.spyOn(tor, 'destroyHiddenService') await connectionsManagerService.init() const network = await connectionsManagerService.getNetwork() - console.log('network', network) expect(network.hiddenService.onionAddress.split('.')[0]).toHaveLength(56) expect(network.hiddenService.privateKey).toHaveLength(99) const peerId = await PeerId.createFromJSON(network.peerId) diff --git a/packages/backend/src/nest/validation/validators.ts b/packages/backend/src/nest/validation/validators.ts index e276487622..fff7900123 100644 --- a/packages/backend/src/nest/validation/validators.ts +++ b/packages/backend/src/nest/validation/validators.ts @@ -2,6 +2,7 @@ import _ from 'validator' import joi from 'joi' import { ChannelMessage, PublicChannel } from '@quiet/types' import { ServerStoredCommunityMetadata } from '../storageServerProxy/storageServerProxy.types' +import { isPSKcodeValid } from '@quiet/common' const messageMediaSchema = joi.object({ path: joi.string().allow(null), @@ -46,7 +47,12 @@ const metadataSchema = joi.object({ rootCa: joi.string().required(), ownerOrbitDbIdentity: joi.string().required(), peerList: joi.array().items(joi.string()).required(), - psk: joi.string().required(), + psk: joi + .string() + .required() + .custom((value, _helpers) => { + return isPSKcodeValid(value) + }), }) export const isUser = (publicKey: string, halfKey: string): boolean => { @@ -74,6 +80,8 @@ export const isChannel = (channel: PublicChannel): boolean => { export const isServerStoredMetadata = (metadata: ServerStoredCommunityMetadata): boolean => { const value = metadataSchema.validate(metadata) + // Leave this log for first iterations of QSS + console.log(value.error) return !value.error } diff --git a/packages/e2e-tests/src/QSS/Dockerfile b/packages/e2e-tests/src/QSS/Dockerfile new file mode 100644 index 0000000000..80b5543413 --- /dev/null +++ b/packages/e2e-tests/src/QSS/Dockerfile @@ -0,0 +1,6 @@ +FROM node:18.12.1@sha256:e9ad817b0d42b4d177a4bef8a0aff97c352468a008c3fdb2b4a82533425480df +RUN git clone https://github.com/TryQuiet/quiet-storage-service.git +WORKDIR /quiet-storage-service +RUN mkdir storage +RUN npm install +CMD JWT_SECRET=101010 npm run start \ No newline at end of file diff --git a/packages/e2e-tests/src/QSS/README.md b/packages/e2e-tests/src/QSS/README.md new file mode 100644 index 0000000000..7a1edd023f --- /dev/null +++ b/packages/e2e-tests/src/QSS/README.md @@ -0,0 +1,4 @@ +## Run qss for e2e test + +docker build -t qss . +docker run -p 3000:3000 -v $(pwd)/storage/:/quiet-storage-service/storage/ qss \ No newline at end of file diff --git a/packages/e2e-tests/src/QSS/storage/QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG.json b/packages/e2e-tests/src/QSS/storage/QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG.json new file mode 100644 index 0000000000..717a1fa501 --- /dev/null +++ b/packages/e2e-tests/src/QSS/storage/QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG.json @@ -0,0 +1,10 @@ + { + "id": "id", + "rootCa": "rootCa", + "ownerCertificate": "ownerCertificate", + "ownerOrbitDbIdentity": "ownerOrbitDbIdentity", + "peerList": [ + "/dns4/pgzlcstu4ljvma7jqyalimcxlvss5bwlbba3c3iszgtwxee4qjdlgeqd.onion/tcp/80/ws/p2p/QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG" + ], + "psk": "5T9GBVpDoRpKJQK4caDTz5e5nym2zprtoySL2oLrzr4=" + } \ No newline at end of file diff --git a/packages/e2e-tests/src/tests/joiningWithQSS.test.ts b/packages/e2e-tests/src/tests/joiningWithQSS.test.ts new file mode 100644 index 0000000000..6b63ba0334 --- /dev/null +++ b/packages/e2e-tests/src/tests/joiningWithQSS.test.ts @@ -0,0 +1,118 @@ +import { composeInvitationShareUrl, createLibp2pAddress } from '@quiet/common' +import { + App, + Channel, + CreateCommunityModal, + JoinCommunityModal, + JoiningLoadingPanel, + RegisterUsernameModal, +} from '../selectors' +import { InvitationDataV2, InvitationDataVersion } from '@quiet/types' +interface UserTestData { + username: string + app: App + messages: string[] +} +jest.setTimeout(450000) + +// Run QSS locally before this test +const serverAddress = 'http://127.0.0.1:3000' +const data: InvitationDataV2 = { + version: InvitationDataVersion.v2, + cid: 'QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG', + token: '898989', + serverAddress: serverAddress, + inviterAddress: 'pgzlcstu4ljvma7jqyalimcxlvss5bwlbba3c3iszgtwxee4qjdlgeqd', +} + +const invitationCode = decodeURIComponent(composeInvitationShareUrl(data)) + +describe('User joining with storage server', () => { + let users: Record + const communityName = 'Filmmakers' + let generalChannelOwner: Channel + + beforeAll(async () => { + users = { + owner: { + username: 'owner', + messages: ['Hi', 'Hello', 'After guest left the app'], + app: new App(), + }, + user1: { + username: 'user-joining-1', + messages: ['Nice to meet you all'], + app: new App(), + }, + } + }) + + afterAll(async () => { + await users.owner.app.close() + await users.user1.app.close() + }) + + describe.skip('Owner creates the community', () => { + // Skip, at this moment we can't fully connect without having access to owner's link + it('Owner opens the app', async () => { + await users.owner.app.open() + }) + + it('Owner sees "join community" modal and switches to "create community" modal', async () => { + const joinModal = new JoinCommunityModal(users.owner.app.driver) + const isJoinModal = await joinModal.element.isDisplayed() + expect(isJoinModal).toBeTruthy() + await joinModal.switchToCreateCommunity() + }) + it('Owner submits valid community name', async () => { + const createModal = new CreateCommunityModal(users.owner.app.driver) + const isCreateModal = await createModal.element.isDisplayed() + expect(isCreateModal).toBeTruthy() + await createModal.typeCommunityName(communityName) + await createModal.submit() + }) + it('Owner sees "register username" modal and submits valid username', async () => { + const registerModal = new RegisterUsernameModal(users.owner.app.driver) + const isRegisterModal = await registerModal.element.isDisplayed() + expect(isRegisterModal).toBeTruthy() + await registerModal.typeUsername(users.owner.username) + await registerModal.submit() + }) + it('Owner registers successfully and sees general channel', async () => { + generalChannelOwner = new Channel(users.owner.app.driver, 'general') + const isGeneralChannel = await generalChannelOwner.element.isDisplayed() + const generalChannelText = await generalChannelOwner.element.getText() + expect(isGeneralChannel).toBeTruthy() + expect(generalChannelText).toEqual('# general') + }) + }) + + describe('Guest joins using v2 invitation link', () => { + it('Guest opens the app', async () => { + await users.user1.app.open() + }) + it('Guest submits invitation code received from owner', async () => { + const joinCommunityModal = new JoinCommunityModal(users.user1.app.driver) + const isJoinCommunityModal = await joinCommunityModal.element.isDisplayed() + expect(isJoinCommunityModal).toBeTruthy() + console.log({ invitationCode }) + await joinCommunityModal.typeCommunityCode(invitationCode) + await joinCommunityModal.submit() + }) + + it('Guest submits valid username', async () => { + const registerModal = new RegisterUsernameModal(users.user1.app.driver) + const isRegisterModal = await registerModal.element.isDisplayed() + expect(isRegisterModal).toBeTruthy() + await registerModal.clearInput() + await registerModal.typeUsername(users.user1.username) + await registerModal.submit() + }) + + it('Guest sees the joining panel', async () => { + // TODO: finish joining process after implementing retrieving/displaying v2 invitation link + const joiningPanel = new JoiningLoadingPanel(users.user1.app.driver) + await joiningPanel.element.isDisplayed() + }) + }) +})