Commit Graph
1 Commits
Author SHA1 Message Date
JMR-devandClaude Opus 5 92bcff8656 Make a failure that says nothing still say something (#193)
Three sites, all ci == 0 before this, and all the same rule:

  work/ConversionWorker.kt:316        cause.message ?: GENERIC_FAILURE_MESSAGE
  convert/ConversionViewModel.kt:631  e.message ?: SAVE_FAILED_MESSAGE
  join/JoinViewModel.kt:416           e.message ?: SAVE_FAILED_MESSAGE

Every existing test throws WITH a message, so the right-hand side had never been evaluated
anywhere in the suite. A Throwable carrying none is not exotic: RuntimeException(),
IOException() and most platform exceptions raised without an argument all have a null
message, and a native engine that dies is exactly where one comes from.

The worker case needed care, and the care is the reason it survived three waves.
ConversionStateMappingTest's "a failure with nothing said still says something" looks like
it covers that site and does not -- it drives the READ side, map(FAILED, Data.EMPTY), and
that side has a fallback of its own at ConversionViewModel.kt:147-149 which turns a blank
KEY_ERROR back into the same constant. So a test asserting on the resulting Failed state
stays green while the worker's fallback is broken. Measured rather than reasoned: with
:316 mutated to .orEmpty(), exactly ONE of 587 tests went red, and it was the new one.
Everything else, including the test that appears to cover it, stayed green.

So the worker case reads KEY_ERROR off the worker's own Result, before anything downstream
can repair it. The two save cases have no such second line -- both write _state.value
directly -- so the state is the right thing to assert there, and both also assert that
`pending` still travels: a fallback that dropped the handle would leave the file
unreachable from the very screen that just said the save failed.

Held in one class against the ticket's suggestion of three. They are one rule at three
layers, and the masking above has to be explained once rather than three times.
FailedSaveRetryTest already sets the precedent for both ViewModels in one file; this adds
one worker to that shape.

FORCE_SOFTWARE in the worker fixture so the failure comes straight out of runFFmpeg. AUTO
would enter runMedia3OrFallBack, whose catch runs the job a second time in software: the
same exception arrives, but by a path this is not about and which HardwareFallbackTest owns.

Mutations, all run and restored -- each site to .orEmpty(), never to a different constant,
which would only prove the test reads a constant:

  ConversionWorker:316      1 of 587 red (this file)
  ConversionViewModel:631   red
  JoinViewModel:416         red

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 19:36:27 -05:00