export {}; /** * Subscriber rows must never depend on a stable device identifier. * * The service stopped serialising `deviceId` (privacy ruling); the admin still * has to tell two phones apart well enough to revoke the right one, without any * token fragment or identifier. This is a source contract because the row is * JSX with no headless renderer in this project. */ declare const __dirname: string; const fs: { readFileSync(p: string, enc: string): string } = jest.requireActual('fs'); const path: { join(...parts: string[]): string } = jest.requireActual('path'); const source = fs.readFileSync( path.join(__dirname, '..', 'src', 'screens', 'AlertDetailScreen.tsx'), 'utf8', ); describe('subscriber row discriminator', () => { it('does not read deviceId or the removed joined field', () => { expect(source).not.toMatch(/sub\.deviceId/); expect(source).not.toMatch(/sub\.joined\b/); }); it('uses the subscription id as the revocation handle', () => { expect(source).toMatch(/key=\{sub\.id\}/); expect(source).toMatch(/subscriptionId: sub\.id/); }); it('only reloads after a successful revoke and surfaces failures', () => { const pressAt = source.indexOf('onPress={async () => {'); expect(pressAt).toBeGreaterThan(-1); const handler = source.slice(pressAt, pressAt + 700); // reloadSubs() lives in the try, after the awaited removal, so the row // cannot vanish optimistically when the service rejects the request. expect(handler.indexOf('await api.removeSubscription')).toBeLessThan( handler.indexOf('reloadSubs()'), ); expect(handler).toContain('setError('); }); it('names a device by role label + server order, never a token fragment', () => { const row = source.slice(source.indexOf('const deviceName'), source.indexOf('const deviceName') + 900); expect(row).toMatch(/index \+ 1/); expect(row).toMatch(/joined \{joinedAgo\}/); expect(row).not.toMatch(/token/i); }); });