mirror of
https://github.com/yattee/yattee.git
synced 2026-10-24 13:42:54 +00:00
Refuse to repair a context that is not this identity's
Foreground activation repairs a selection whose account this device knows was deleted. The check for whether that is safe was the identity scope being resolved, which it already is in the window between an Apple account change moving the namespace and the reload refilling memory — so the repair weighed the previous identity's selection against the previous identity's tombstones and announced the result to a user who had not been shown an account yet. Fenced on the loaded namespace, the same way saves are. Three smaller things around the same theme: - The destination rows forgot their membership answers on a credential or account change but not on an instance change, which adds a row to the picker while it is open. Unknown reads as unsubscribed, so that row offered to subscribe a channel the account already has. - The subscriptions settings count applied whatever the fetch returned. A selection change is fenced by the service's provider generation, but the fallback to local is driven by availability and announces nothing, so the context is captured before the fetch and rechecked after it. - Deleting a source from the browser runs the same cascade as deleting it from Settings, and was the one dialog left that did not say the logins go with it.
This commit is contained in:
@@ -666,6 +666,53 @@ struct DataContextValidatorTests {
|
||||
#expect(!accounts.pendingContextUpload)
|
||||
}
|
||||
|
||||
@Test("Nothing is repaired while the namespace is reloading")
|
||||
func correctionIsFencedToTheLoadedNamespace() {
|
||||
let instances = InstancesManager()
|
||||
let instance = Instance(type: .invidious, url: URL(string: "https://inv-reload.example.com")!)
|
||||
instances.add(instance)
|
||||
defer { instances.remove(instance) }
|
||||
|
||||
let scope = CredentialIdentityScope(fixedScope: "identity-A")
|
||||
let toasts = ToastManager()
|
||||
let accounts = AccountsManager(
|
||||
identityScope: scope,
|
||||
instancesManager: instances,
|
||||
defaults: UserDefaults(suiteName: "yattee.tests.\(UUID().uuidString)")!
|
||||
)
|
||||
let validator = DataContextValidator(
|
||||
accountsManager: accounts,
|
||||
instancesManager: instances,
|
||||
toastManager: toasts
|
||||
)
|
||||
|
||||
let deleted = accounts.addAccount(instanceID: instance.id)
|
||||
let sibling = accounts.addAccount(instanceID: instance.id)
|
||||
accounts.setStatus(.signedIn, for: sibling.id)
|
||||
accounts.setContext(.local)
|
||||
accounts.applyRemoteTombstone(deleted.id)
|
||||
accounts.setContext(.account(deleted.id))
|
||||
|
||||
// The scope moved before the observer that reloads the manager ran, so
|
||||
// the selection and the tombstone are both still identity-A's — and the
|
||||
// identity now resolved is identity-B, which chose neither.
|
||||
scope.setFixedScope("identity-B", announcing: false)
|
||||
validator.correctIfContextRemoved()
|
||||
|
||||
#expect(accounts.currentContext == .account(deleted.id))
|
||||
// Least of all announced: a toast here tells the user their account was
|
||||
// switched during a reload that has not shown them an account yet.
|
||||
#expect(toasts.activeToasts.isEmpty)
|
||||
|
||||
// Back under the namespace the state in memory came from, the same
|
||||
// repair runs exactly as it always did.
|
||||
scope.setFixedScope("identity-A", announcing: false)
|
||||
validator.correctIfContextRemoved()
|
||||
|
||||
#expect(accounts.currentContext == .account(sibling.id))
|
||||
#expect(!toasts.activeToasts.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Only accounts the app can actually resolve are offered")
|
||||
func availableContextsAreFiltered() {
|
||||
let instances = InstancesManager()
|
||||
|
||||
Reference in New Issue
Block a user