Pro: QA backend override, a proof-acquisition floor, and a unit-test class-init fix - #2153
Open
mpretty-cyro wants to merge 6 commits into
Open
Conversation
Lets a QA Pro backend be targeted without rebuilding, matching the iOS customProBackendUrl/customProBackendPubkey launch variables. Both values are required together: a QA URL paired with the production signing key reads every QA-signed proof as invalid and silently strips Pro content.
…ation
Reading a SessionProtocol constant runs System.loadLibrary("session_util"), so
doing it from the companion's initialiser made the class impossible to
initialise wherever the native library is absent -- every JVM unit test. Mockito
could not instrument it, and the nine tests constructing a ConversationViewModel
failed with NoClassDefFoundError.
The constants stay single-sourced from libsession; they are just read on first
use rather than on class load.
jagerman
force-pushed
the
feature/pro-backend-override-qa
branch
from
August 6, 2026 18:08
924aa0b to
5906064
Compare
A successful generate force-refreshes get_pro_status, which asks libsession for a renewal target, and `proofExpiry - PRO_RENEWAL_LEAD` is permanently in the past for any proof living less than the 60-minute lead -- so the worker rescheduled itself immediately and looped. Mirrors iOS SessionProManager.reconcileProofRenewal and Desktop, constants included: 60s while covered, 15s * attempt capped at 900s while dark, and re-arming rather than dropping the work, since `target <= now` is also the normal renewal-due signal. The state is in-memory as it is on the other two platforms; a process restart costs one extra request rather than a loop.
The debug menu already drives these states through preferences that ProStatusManager and ProSettingsViewModel read; they were just unreachable from an automated launch, so the Appium suite could only cover Pro screens on iOS. sessionProBackendStatus and sessionProLoadingState are named for the state being simulated rather than for the preference behind them, matching the keys iOS already accepts, so one cross-platform test has one setup that means the same thing on both. `useActual` clears an override. Values are mapped explicitly rather than derived from enum names, so renaming a case cannot silently change what a test asks for.
Two of the three EXPIRING labels claimed 14 days while the code used 2, which is how a reader (and a test author) ends up with the wrong value: the label looks authoritative and is the first thing you see. EXPIRING_LATER moves 40 -> 30 days so both platforms can assert the same rendered string. It already sat outside the 7-day window that gates the expiring CTA and still does, so its behaviour is unchanged -- EXPIRING keeps its 2 days precisely because it is inside that window and is the only way to trigger the CTA by hand.
It sat on the ComposeView host in XML while the tap target is the Avatar inside it. Compose publishes its own semantics tree, so whether the host's description survived depended on composition timing -- intermittently leaving the avatar unlabelled for accessibility services, and unfindable by anything addressing it by description. Removed from the XML rather than left in both places: the same description on two nodes of one tree is the ambiguity being fixed, not redundancy. Nothing read it there -- the id is used as a constraint anchor and for setThemedContent only.
mpretty-cyro
marked this pull request as ready for review
August 7, 2026 07:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent changes, all Pro-adjacent.
QA backend override
Lets a QA Pro backend be targeted without rebuilding, via
QaLaunchConfig— matching iOS'scustomProBackendUrl/customProBackendPubkeylaunch variables, so the Appium suite canpoint both platforms at the same backend with one switch.
Both values are required together: a QA URL paired with the production signing key reads
every QA-signed proof as invalid and silently strips Pro content.
Proof-acquisition floor
ProProofGenerationWorkerhad no floor, and the path is a closed loop: a successfulgenerate force-refreshes
get_pro_status, the fetch asks libsession for a renewal target,and
target = proofExpiry - PRO_RENEWAL_LEADis permanently in the past whenever a prooflives for less than the 60-minute lead — so it reschedules immediately, forever. Measured at
~9-11 requests/second against a backend issuing 5-minute proofs.
The floor mirrors iOS (
SessionProManager.reconcileProofRenewal) and Desktop(
state/ducks/proBackendData.ts) exactly —60swhile covered,15s × attemptcapped at900swhile dark,darkAttemptreset on cover. The constants carry a comment naming them across-client contract so nobody tunes one in isolation.
Two properties worth preserving in any refactor:
target <= nowis the normal "renewal due" signal and dropping it would break realrenewals.
purchase, or a server-side grant the backend hasn't ingested — settles at 15-minute
spacing rather than hammering.
Placed at the top of
doWork()rather than in the scheduler, which covers every paththat can schedule acquisition (both
FetchProStatusWorkersites andProStatusManager.onPurchaseInFlight) rather than one branch, and matches where iOS putsit. State lives in the existing
pro_statename/value table, persisted rather thanin-memory because WorkManager can run the worker across process death and an in-memory
counter would reset and defeat the pacing. No migration.
Unit-test class-init fix
Reading a
SessionProtocolconstant runsSystem.loadLibrary("session_util"), so doing itfrom
ProStatusManager's companion initialiser made the class impossible to initialisewherever the native library is absent — every JVM unit test. Mockito could not instrument
it, and the nine tests constructing a
ConversationViewModelfailed withNoClassDefFoundError.The constants stay single-sourced from libsession; they are just read on first use rather
than on class load.
Testing
assemblePlayDebugandassemblePlayAutomaticQagreen. 198/198 unit tests pass (was189/198 before the class-init fix).