Files
cinny/src/app/utils/updateErrors.test.ts
T
Lotus CIandClaude Opus 5.5 27d659118f
CI / Build & Quality Checks (pull_request) Failing after 1m40s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Playwright smoke (e2e) (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 6s
fix(desktop updates): Linux package installs update via their package manager
Reported on CachyOS: "Check for Updates → The update downloaded but
couldn't be installed … Permission denied (os error 13) at path
/usr/bin/tauri_current_app…". The app was installed from the Arch package;
Tauri's Linux updater can only replace an AppImage.

With cinny-desktop's new update_install_kind command:
- pacman / deb installs: the toast says the update is available and opens
  Settings → General → App Updates, which shows the package-manager command
  (`sudo pacman -U …pkg.tar.zst`, or the .deb + `sudo apt install`) with
  Copy command and Download package — no Install & Restart that can't work.
  "Copied" only when the clipboard write actually succeeded.
- other distros: a link to the downloads page.
- Windows, AppImage, and desktop builds without the command: unchanged
  in-app update.
- a native "package-managed" refusal shows the same help.

Tests: unit (kinds, commands, refusal detection); in the real client with a
simulated desktop bridge: pacman → toast + Settings command/buttons,
install never attempted; older desktop → in-app flow as before. Unit 1321,
Playwright 26 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-30 19:00:10 -04:00

90 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
describeUpdateError,
manualDownloadUrl,
MANUAL_DOWNLOAD_URL,
parseUpdateError,
} from './updateErrors';
test('parseUpdateError reads the native phase prefix', () => {
assert.deepEqual(parseUpdateError('download: error sending request for url (x)', 'check'), {
phase: 'download',
message: 'error sending request for url (x)',
});
assert.deepEqual(parseUpdateError('install: access denied', 'download'), {
phase: 'install',
message: 'access denied',
});
});
test('parseUpdateError falls back to the caller phase for unprefixed errors', () => {
assert.deepEqual(parseUpdateError('error sending request', 'check'), {
phase: 'check',
message: 'error sending request',
});
});
test('the reported download failure reads as a busy server, not a failed check', () => {
const { phase, message } = parseUpdateError(
'download: error sending request for url (https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64-setup.nsis.zip)',
'check',
);
const text = describeUpdateError(phase, message);
assert.match(text, /didn’t finish downloading/);
assert.match(text, /download the installer yourself/);
});
test('only signature failures are reported as a failed safety check', () => {
assert.match(
describeUpdateError('download', 'The signature X could not be decoded'),
/safety check/,
);
assert.doesNotMatch(describeUpdateError('download', 'No space left on device'), /safety/);
});
test('check failures never suggest the app is broken', () => {
assert.match(describeUpdateError('check', 'operation timed out'), /Lotus Chat still works/);
assert.match(describeUpdateError('check', 'weird'), /Lotus Chat still works/);
});
test('manual download link: Windows gets the installer, others the release page', () => {
assert.equal(
manualDownloadUrl('Mozilla/5.0 (Windows NT 10.0; Win64; x64) Edg/140'),
MANUAL_DOWNLOAD_URL.windows,
);
assert.equal(manualDownloadUrl('Mozilla/5.0 (X11; Linux x86_64)'), MANUAL_DOWNLOAD_URL.other);
});
test('install kinds: anything unknown keeps the in-app updater', async () => {
const { toInstallKind } = await import('./updateErrors');
assert.equal(toInstallKind('pacman'), 'pacman');
assert.equal(toInstallKind('deb'), 'deb');
assert.equal(toInstallKind('manual'), 'manual');
assert.equal(toInstallKind('in-app'), 'in-app');
assert.equal(toInstallKind(undefined), 'in-app', 'older desktop build without the command');
assert.equal(toInstallKind('rpm'), 'in-app');
});
test('package installs get their package manager’s command, not an Install button', async () => {
const { packageUpdateHelp, isPackageManagedError } = await import('./updateErrors');
const pacman = packageUpdateHelp('pacman')!;
assert.equal(
pacman.command,
'sudo pacman -U https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.pkg.tar.zst',
);
assert.match(pacman.url, /LotusChat-x86_64\.pkg\.tar\.zst$/);
const deb = packageUpdateHelp('deb')!;
assert.match(deb.command!, /sudo apt install \.\/LotusChat-x86_64\.deb$/);
assert.equal(packageUpdateHelp('manual')?.command, undefined);
assert.equal(packageUpdateHelp('in-app'), undefined);
assert.equal(
isPackageManagedError('package-managed (pacman): update Lotus Chat with your package manager'),
true,
);
assert.equal(
isPackageManagedError('Permission denied (os error 13) at path "/usr/bin/tauri_current_app"'),
false,
);
});