// Applying a staged bundle — the one thing that must not need a cable.
//
// ⛔ THE DEFECT. "restart" was `stopNode(); startNode()`: terminate the worklet, start a new one,
// inside the SAME process. Measured on a handset — a staged bundle pressed through that path never
// became the running one, while `adb shell am force-stop` plus a relaunch applied it every time.
//
// ⛔ WHY IT IS NOT A DETAIL. `chooseBundle` runs at worklet start and nowhere else, so a restart that
// does not really restart means NO update can be applied from the device: every one needs a cable
// and somebody at a desk. That is a demo of an update mechanism, not one.
//
// ⚠ These pin the CHOICE, not the Kotlin. What Android does when asked cannot be asserted from here;
// that it IS asked, and that a refusal is neither hidden nor fatal, is this file's business.
import test from 'brittle'
import { planApply, RESTART_DELAY_MS } from '../lib/host/apply-plan.js'
test('★★ applying an update asks for a PROCESS restart', async (t) => {
const asked = []
const r = await planApply({
native: { restartProcess: async (ms) => { asked.push(ms) } },
restartNode: async () => t.fail('the worklet restart must not run when the process one worked')
})
t.is(r.restarted, 'process', 'the process, not the thread')
t.alike(asked, [RESTART_DELAY_MS], 'asked exactly once')
t.ok(RESTART_DELAY_MS >= 150, 'with enough delay for the reply to cross the bridge first')
t.absent(r.degraded)
})
test('⚠ a device that refuses falls back, and SAYS it was degraded', async (t) => {
const said = []
let fell = 0
const r = await planApply({
native: { restartProcess: async () => { throw new Error('no alarm permission') } },
restartNode: async () => { fell++ },
log: (m) => said.push(m)
})
t.is(r.restarted, 'worklet', 'the weaker restart still happens')
t.is(fell, 1)
t.ok(r.degraded, 'and the caller is told — silently doing the weaker one is how this hid')
t.ok(/falling back/.test(said[0].msg), said[0].msg)
})
// ⛔ An APK too old to restart itself. It must say so: this is the shell that can never apply an
// update, and the whole point is that nothing else can tell.
test('⛔ a shell with no such method says updates will not apply', async (t) => {
const said = []
let fell = 0
const r = await planApply({ native: {}, restartNode: async () => { fell++ }, log: (m) => said.push(m) })
t.is(r.restarted, 'worklet')
t.ok(r.degraded)
t.is(fell, 1)
t.ok(/cannot restart its own process/.test(said[0].msg), said[0].msg)
})
test('⚠ no native module at all is still not a crash', async (t) => {
const r = await planApply({ native: null, restartNode: async () => {} })
t.is(r.restarted, 'worklet')
})