/** * Unit tests for backups/restic-script.ts — the pure shell-command builders and * exit-marker reader used by the restic helper container path. No mocks. */ import { describe, it, expect } from 'bun:test'; import { EXIT_MARKER, shellQuote, resticCommand, finishScript, readExitMarker, buildHelperEnv, buildHelperBinds, localRepoGuard, localRepoChown, classifyProcClose, classifyProcError, gcsCredentialPreamble, GCS_CRED_FILE, tlsCertPreamble, CACERT_FILE, TLS_CLIENT_FILE, } from '../../src/lib/server/backups/restic-script'; describe('shellQuote', () => { it('wraps a plain string in single quotes', () => { expect(shellQuote('data')).toBe(`'data'`); }); it('escapes embedded single quotes (no shell injection)', () => { expect(shellQuote(`a'b`)).toBe(`'a'\\''b'`); }); it('neutralises shell metacharacters by quoting', () => { expect(shellQuote('$(rm -rf /)')).toBe(`'$(rm -rf /)'`); expect(shellQuote('a; b')).toBe(`'a; b'`); }); }); describe('resticCommand', () => { it('builds a restic command with every arg quoted', () => { expect(resticCommand(['backup', '/volumes/', '--tag', 'x'])).toBe( `restic 'backup' '/volumes/' '--tag' 'x'` ); }); it('quotes an arg that contains a single quote', () => { expect(resticCommand(['--host', `o'brien`])).toBe(`restic '--host' 'o'\\''brien'`); }); }); describe('finishScript + readExitMarker (surface the REAL restic exit code)', () => { it('appends the exit marker capturing $?', () => { const s = finishScript(`restic 'backup'`); expect(s).toContain('restic \'backup\';'); expect(s).toContain('__rc=$?'); expect(s).toContain(`echo "${EXIT_MARKER}`); }); it('reads the exit code from the marker line', () => { expect(readExitMarker(`some output\n${EXIT_MARKER}0`)).toBe(0); expect(readExitMarker(`${EXIT_MARKER}3`)).toBe(3); // partial-backup code preserved expect(readExitMarker(`x\n${EXIT_MARKER}1\ny`)).toBe(1); }); it('reads the LAST marker when output has trailing lines', () => { expect(readExitMarker(`${EXIT_MARKER}1\ntrailing\n${EXIT_MARKER}0`)).toBe(0); }); // A caller-supplied script (e.g. the in-place restore swap) uses `set -e`. // Wrapping it as `finishScript('(