Skip to content

Commit 17056f5

Browse files
committed
fix tests; move qss url emit to nse to qss.service
1 parent 5b5336d commit 17056f5

11 files changed

Lines changed: 188 additions & 214 deletions

File tree

packages/backend/src/nest/auth/sigchain.service.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,14 @@ export class SigChainService extends EventEmitter {
143143

144144
private handleChainUpdate = (teamName: string) => {
145145
this._updateUsersOnChainUpdate(teamName)
146-
void this._updateKeysOnChainUpdate(teamName)
146+
void this._updateKeysOnChainUpdate(teamName).catch(err => {
147+
this.logger.error('Failed to update iOS keychain on chain update', err)
148+
})
147149
this._updateDeviceCredentials(teamName)
148150
this.emit('updated', teamName)
149-
this.saveChain(teamName)
151+
void this.saveChain(teamName).catch(err => {
152+
this.logger.error('Failed to save chain after update', err)
153+
})
150154
this.logger.info('Chain updated, emitted updated event')
151155
}
152156

@@ -185,6 +189,7 @@ export class SigChainService extends EventEmitter {
185189
}
186190

187191
const teamId = sigchain.team!.id
192+
await this._ensureDb()
188193
const alreadySentKeys: Set<string> = new Set(await this.localDbService.getKeysStoredInKeychain(teamId))
189194
const keysToSend: StorableKey[] = []
190195
const keyNamesSent: string[] = []

packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -138,99 +138,6 @@ describe('ConnectionsManagerService', () => {
138138
expect(launchSpy).toBeCalledTimes(1)
139139
})
140140

141-
it('emits the authoritative QSS URL for the NSE on iOS', async () => {
142-
const originalPlatform = process.platform
143-
Object.defineProperty(process, 'platform', {
144-
value: 'ios',
145-
})
146-
147-
try {
148-
await localDbService.setCommunity({
149-
...community,
150-
teamId: 'team-id',
151-
qssEndpoint: 'wss://community.example/ws',
152-
})
153-
await localDbService.setCurrentCommunityId(community.id)
154-
155-
qssService._qssEndpoint = 'wss://authoritative.example'
156-
157-
const emitSpy = jest.spyOn(connectionsManagerService.serverIoProvider.io, 'emit')
158-
159-
await (connectionsManagerService as any).emitNseQssUrl()
160-
161-
expect(emitSpy).toHaveBeenCalledWith(SocketEvents.NSE_QSS_URL_UPDATED, {
162-
teamId: 'team-id',
163-
qssUrl: 'https://authoritative.example',
164-
})
165-
} finally {
166-
Object.defineProperty(process, 'platform', {
167-
value: originalPlatform,
168-
})
169-
}
170-
})
171-
172-
it('falls back to the stored community QSS endpoint when no authoritative endpoint is available', async () => {
173-
const originalPlatform = process.platform
174-
Object.defineProperty(process, 'platform', {
175-
value: 'ios',
176-
})
177-
178-
try {
179-
await localDbService.setCommunity({
180-
...community,
181-
teamId: 'team-id',
182-
qssEndpoint: 'ws://community.example/ws',
183-
})
184-
await localDbService.setCurrentCommunityId(community.id)
185-
186-
qssService._qssEndpoint = undefined as any
187-
188-
const emitSpy = jest.spyOn(connectionsManagerService.serverIoProvider.io, 'emit')
189-
190-
await (connectionsManagerService as any).emitNseQssUrl()
191-
192-
expect(emitSpy).toHaveBeenCalledWith(SocketEvents.NSE_QSS_URL_UPDATED, {
193-
teamId: 'team-id',
194-
qssUrl: 'http://community.example/ws',
195-
})
196-
} finally {
197-
Object.defineProperty(process, 'platform', {
198-
value: originalPlatform,
199-
})
200-
}
201-
})
202-
203-
it('skips NSE QSS URL emission when no valid ws or wss endpoint can be derived', async () => {
204-
const originalPlatform = process.platform
205-
Object.defineProperty(process, 'platform', {
206-
value: 'ios',
207-
})
208-
209-
try {
210-
await localDbService.setCommunity({
211-
...community,
212-
teamId: 'team-id',
213-
qssEndpoint: 'https://community.example/api',
214-
})
215-
await localDbService.setCurrentCommunityId(community.id)
216-
217-
qssService._qssEndpoint = 'https://authoritative.example/api'
218-
219-
const emitSpy = jest.spyOn(connectionsManagerService.serverIoProvider.io, 'emit')
220-
221-
await (connectionsManagerService as any).emitNseQssUrl()
222-
223-
expect(emitSpy).not.toHaveBeenCalledWith(
224-
SocketEvents.NSE_QSS_URL_UPDATED,
225-
expect.objectContaining({ teamId: 'team-id' })
226-
)
227-
} finally {
228-
Object.defineProperty(process, 'platform', {
229-
value: originalPlatform,
230-
})
231-
}
232-
})
233-
234141
it('pauses and resumes qss alongside the mobile lifecycle', async () => {
235142
const closeSocketSpy = jest.spyOn(connectionsManagerService, 'closeSocket').mockResolvedValue()
236143
const openSocketSpy = jest.spyOn(connectionsManagerService, 'openSocket').mockResolvedValue()

packages/backend/src/nest/connections-manager/connections-manager.service.ts

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import {
5050
InvitationData,
5151
SetUserProfileResponse,
5252
UserProfilesUpdatedPayload,
53-
NseQssUrlUpdatedEvent,
5453
} from '@quiet/types'
5554
import { CONFIG_OPTIONS, QSS_ALLOWED, QSS_ENDPOINT, SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const'
5655
import { Libp2pService, Libp2pState } from '../libp2p/libp2p.service'
@@ -601,7 +600,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI
601600

602601
// Unblock websocket endpoints
603602
this.socketService.resolveReadyness()
604-
void this.emitNseQssUrl()
605603
this.serverIoProvider.io.emit(SocketEvents.COMMUNITY_LAUNCHED, {
606604
id: community.id,
607605
} as LaunchCommunityPayload)
@@ -687,62 +685,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI
687685
})
688686
}
689687

690-
private getNseQssUrl(wsUrl: string | undefined): string | undefined {
691-
if (wsUrl == null || wsUrl === '') {
692-
this.logger.warn('Skipping NSE QSS URL update because wsUrl is empty')
693-
return undefined
694-
}
695-
696-
if (wsUrl.startsWith('wss://')) {
697-
return `https://${wsUrl.slice('wss://'.length)}`
698-
}
699-
700-
if (wsUrl.startsWith('ws://')) {
701-
return `http://${wsUrl.slice('ws://'.length)}`
702-
}
703-
704-
this.logger.warn('Skipping NSE QSS URL update because endpoint is not ws/wss', wsUrl)
705-
return undefined
706-
}
707-
708-
private async emitNseQssUrl(): Promise<void> {
709-
if ((process.platform as string) !== 'ios') {
710-
return
711-
}
712-
713-
const community = await this.localDbService.getCurrentCommunity()
714-
if (community?.teamId == null) {
715-
this.logger.warn('Skipping NSE QSS URL update because no active community or team ID found')
716-
this.logger.warn('Community', community)
717-
return
718-
}
719-
720-
const wsUrl = community.qssEndpoint ?? this.qssEndpoint ?? this.qssService.qssEndpoint
721-
const qssUrl = this.getNseQssUrl(wsUrl)
722-
if (qssUrl == null) {
723-
this.logger.warn('Skipping NSE QSS URL update because no valid QSS URL could be derived')
724-
return
725-
}
726-
727-
const payload: NseQssUrlUpdatedEvent = {
728-
teamId: community.teamId,
729-
qssUrl,
730-
}
731-
732-
this.serverIoProvider.io.emit(SocketEvents.NSE_QSS_URL_UPDATED, payload)
733-
}
734-
735688
/**
736689
* Attaches listeners for events received from the state manager
737690
*/
738691
private attachSocketServiceListeners() {
739-
this.serverIoProvider.io.on(SocketActions.CONNECTION, socket => {
740-
void this.emitNseQssUrl()
741-
socket.on(SocketActions.START, () => {
742-
void this.emitNseQssUrl()
743-
})
744-
})
745-
746692
// Community
747693
this.socketService.on(SocketActions.CONNECTION, () => {
748694
this.logger.info(`socketService - ${SocketActions.CONNECTION}`)

packages/backend/src/nest/libp2p/integration-tests/userProfile-sync.spec.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ describe('UserProfileStore OrbitDB Sync', () => {
276276
10000,
277277
100
278278
)
279-
userProfileStores[N_PEERS - 1].startSync()
279+
await userProfileStores[N_PEERS - 1].startSync()
280280

281281
await waitForExpect(
282282
async () => {
@@ -291,11 +291,17 @@ describe('UserProfileStore OrbitDB Sync', () => {
291291
1000
292292
)
293293

294-
// Ensure only the new peer emitted 'updated'
295-
for (let i = 0; i < N_PEERS - 1; i++) {
296-
expect(updatedSpies[i]).toHaveBeenCalledTimes(1)
297-
}
298-
logger.info('New peer updated:', updatedSpies[N_PEERS - 1].mock.calls.length, 'times')
299-
expect(updatedSpies[N_PEERS - 1]).toHaveBeenCalled()
294+
await waitForExpect(
295+
async () => {
296+
// Ensure only the new peer emitted 'updated'
297+
for (let i = 0; i < N_PEERS - 1; i++) {
298+
expect(updatedSpies[i]).toHaveBeenCalledTimes(1)
299+
}
300+
logger.info('New peer updated:', updatedSpies[N_PEERS - 1].mock.calls.length, 'times')
301+
expect(updatedSpies[N_PEERS - 1]).toHaveBeenCalled()
302+
},
303+
5000,
304+
100
305+
)
300306
})
301307
})

packages/backend/src/nest/qps/qps.service.spec.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -366,25 +366,6 @@ describe('QPSService', () => {
366366
)
367367
})
368368

369-
it('strips qssUrl from push data before sending to QPS', async () => {
370-
await qpsService.sendBatchPush(TEAM_ID, 'title', 'body', {
371-
cid: 'cid-1',
372-
qssUrl: 'https://untrusted.example',
373-
})
374-
375-
expect(qssClient.sendMessage).toHaveBeenCalledWith(
376-
WebsocketEvents.SEND_BATCH_PUSH,
377-
expect.objectContaining({
378-
payload: expect.objectContaining({
379-
title: 'title',
380-
body: 'body',
381-
data: { teamId: TEAM_ID, cid: 'cid-1' },
382-
}),
383-
}),
384-
true
385-
)
386-
})
387-
388369
it('skips when QPS is disabled', async () => {
389370
const disabled = new QPSService(
390371
false,
@@ -490,26 +471,6 @@ describe('QPSService', () => {
490471
qssClient.sendMessage.mockResolvedValue(pushSuccessResponse)
491472
})
492473

493-
it('strips qssUrl from single push data before sending to QPS', async () => {
494-
await qpsService.sendPush('ucan-user-a', 'title', 'body', {
495-
cid: 'cid-1',
496-
qssUrl: 'https://untrusted.example',
497-
})
498-
499-
expect(qssClient.sendMessage).toHaveBeenCalledWith(
500-
WebsocketEvents.SEND_PUSH,
501-
expect.objectContaining({
502-
payload: {
503-
ucan: 'ucan-user-a',
504-
title: 'title',
505-
body: 'body',
506-
data: { cid: 'cid-1' },
507-
},
508-
}),
509-
true
510-
)
511-
})
512-
513474
it('skips single push when QSS is not connected', async () => {
514475
qssClient.connected = false
515476

packages/backend/src/nest/qss/qss.service.spec.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,85 @@ describe('QSSService', () => {
197197
expect(qssService.canConnect).toBeTruthy()
198198
})
199199

200+
it('emits the NSE QSS URL from the endpoint passed to connect on iOS', async () => {
201+
const originalPlatform = process.platform
202+
Object.defineProperty(process, 'platform', { value: 'ios' })
203+
204+
try {
205+
await localDbService.setCommunity({
206+
...community,
207+
teamId: 'team-id',
208+
qssEnabled: true,
209+
})
210+
await localDbService.setCurrentCommunityId(community.id)
211+
212+
mockedAllowed = jest.spyOn(qssService, 'qssAllowed', 'get').mockReturnValue(true)
213+
const emitSpy = jest.spyOn(qssService['socketService'].serverIoProvider.io, 'emit')
214+
215+
await qssService.connect('wss://community.example/ws')
216+
217+
expect(emitSpy).toHaveBeenCalledWith(SocketEvents.NSE_QSS_URL_UPDATED, {
218+
teamId: 'team-id',
219+
qssUrl: 'https://community.example/ws',
220+
})
221+
} finally {
222+
Object.defineProperty(process, 'platform', { value: originalPlatform })
223+
}
224+
})
225+
226+
it('emits the NSE QSS URL from the stored endpoint when connect is called without one on iOS', async () => {
227+
const originalPlatform = process.platform
228+
Object.defineProperty(process, 'platform', { value: 'ios' })
229+
230+
try {
231+
await localDbService.setCommunity({
232+
...community,
233+
teamId: 'team-id',
234+
qssEnabled: true,
235+
})
236+
await localDbService.setCurrentCommunityId(community.id)
237+
238+
qssService._qssEndpoint = 'ws://configured.example/ws'
239+
mockedAllowed = jest.spyOn(qssService, 'qssAllowed', 'get').mockReturnValue(true)
240+
const emitSpy = jest.spyOn(qssService['socketService'].serverIoProvider.io, 'emit')
241+
242+
await qssService.connect(undefined)
243+
244+
expect(emitSpy).toHaveBeenCalledWith(SocketEvents.NSE_QSS_URL_UPDATED, {
245+
teamId: 'team-id',
246+
qssUrl: 'http://configured.example/ws',
247+
})
248+
} finally {
249+
Object.defineProperty(process, 'platform', { value: originalPlatform })
250+
}
251+
})
252+
253+
it('skips NSE QSS URL emission when connect uses a non-ws endpoint on iOS', async () => {
254+
const originalPlatform = process.platform
255+
Object.defineProperty(process, 'platform', { value: 'ios' })
256+
257+
try {
258+
await localDbService.setCommunity({
259+
...community,
260+
teamId: 'team-id',
261+
qssEnabled: true,
262+
})
263+
await localDbService.setCurrentCommunityId(community.id)
264+
265+
mockedAllowed = jest.spyOn(qssService, 'qssAllowed', 'get').mockReturnValue(true)
266+
const emitSpy = jest.spyOn(qssService['socketService'].serverIoProvider.io, 'emit')
267+
268+
await qssService.connect('https://community.example/api')
269+
270+
expect(emitSpy).not.toHaveBeenCalledWith(
271+
SocketEvents.NSE_QSS_URL_UPDATED,
272+
expect.objectContaining({ teamId: 'team-id' })
273+
)
274+
} finally {
275+
Object.defineProperty(process, 'platform', { value: originalPlatform })
276+
}
277+
})
278+
200279
it(`doesn't connect to QSS when not enabled and an endpoint string is provided`, async () => {
201280
await initCommunity()
202281
mockedAllowed = jest.spyOn(qssService, 'qssAllowed', 'get').mockReturnValue(false)

0 commit comments

Comments
 (0)