sync: report why discovery failed after a browser approval
Every non-Found outcome was collapsed into NO_CALENDARS, so a server whose login flow named an unresolvable host told the user their account holds no task lists. Both Failed and NotCalDav already carry the cause discovery worked out; forward it and keep NO_CALENDARS for the outcomes that really mean it. This is what turned the overwrite.cli.url skew from confusing into undiagnosable: the user retries, gets the same wrong answer, and leaves another spent app password in their device list each time.
This commit is contained in:
@@ -231,10 +231,15 @@ class AddAccountViewModel @Inject constructor(
|
|||||||
result.credentials.server,
|
result.credentials.server,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (outcome is CalDavDiscovery.Outcome.Found) {
|
when (outcome) {
|
||||||
onDiscovered(outcome)
|
is CalDavDiscovery.Outcome.Found -> onDiscovered(outcome)
|
||||||
} else {
|
// ⚠️ Forward what actually happened. Reporting every
|
||||||
backToServer(CalDavDiscovery.Outcome.Cause.NO_CALENDARS)
|
// outcome as NO_CALENDARS tells someone whose server
|
||||||
|
// named an unresolvable host that their account holds
|
||||||
|
// no task lists, which is both wrong and unactionable.
|
||||||
|
is CalDavDiscovery.Outcome.Failed -> backToServer(outcome.cause)
|
||||||
|
is CalDavDiscovery.Outcome.NotCalDav -> backToServer(outcome.cause)
|
||||||
|
else -> backToServer(CalDavDiscovery.Outcome.Cause.NO_CALENDARS)
|
||||||
}
|
}
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,40 @@ class AddAccountViewModelTest {
|
|||||||
.isEqualTo("https://dav.example.com/")
|
.isEqualTo("https://dav.example.com/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a discovery failure after approval reports what actually failed`() =
|
||||||
|
runTest(dispatcher) {
|
||||||
|
// The first outcome is the unauthenticated probe that sends us
|
||||||
|
// into the browser flow; the second is the discovery that runs
|
||||||
|
// once the credentials come back.
|
||||||
|
gateway.discoveryOutcomes += CalDavDiscovery.Outcome.NeedsAuthentication(
|
||||||
|
listOf("cloud.example.com"),
|
||||||
|
)
|
||||||
|
gateway.loginFlow = flow()
|
||||||
|
gateway.pollResults += NextcloudLoginFlow.PollResult.Approved(
|
||||||
|
NextcloudLoginFlow.Credentials(
|
||||||
|
server = "https://cloud.example.com/".toHttpUrl(),
|
||||||
|
loginName = "me",
|
||||||
|
appPassword = "app-pw",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
gateway.discoveryOutcomes += CalDavDiscovery.Outcome.Failed(
|
||||||
|
CalDavDiscovery.Outcome.Cause.UNREACHABLE,
|
||||||
|
"nextcloud: nodename nor servname provided",
|
||||||
|
)
|
||||||
|
|
||||||
|
val vm = viewModel()
|
||||||
|
vm.onServerInputChanged("https://cloud.example.com/")
|
||||||
|
vm.onServerSubmitted()
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// ⚠️ Collapsing this to NO_CALENDARS tells someone with a DNS
|
||||||
|
// failure that their account holds no task lists — wrong, and
|
||||||
|
// nothing they can act on.
|
||||||
|
val step = vm.state.value.step as AddAccountStep.EnterServer
|
||||||
|
assertThat(step.error).isEqualTo(CalDavDiscovery.Outcome.Cause.UNREACHABLE)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `the login URL is handed to the browser exactly once`() = runTest(dispatcher) {
|
fun `the login URL is handed to the browser exactly once`() = runTest(dispatcher) {
|
||||||
gateway.discoveryOutcomes += CalDavDiscovery.Outcome.NeedsAuthentication(emptyList())
|
gateway.discoveryOutcomes += CalDavDiscovery.Outcome.NeedsAuthentication(emptyList())
|
||||||
|
|||||||
Reference in New Issue
Block a user