mirror of
https://github.com/sotashimozono/obsidian-remote-ssh.git
synced 2026-07-22 06:52:07 +00:00
- tests/errorMessage.test.ts (new): covers both branches of errorMessage() (Error instance → .message, non-Error → String()) and both branches of asError() (Error passthrough, non-Error wrapping), including subclasses, null, undefined, and numeric inputs. - tests/RpcError.test.ts (new): covers RpcError constructor (name/code/ message/data set correctly, optional data omitted), instanceof Error, and is() true/false branches with close numeric codes. Also bundles isPreconditionFailed() from src/proto/rpcError.ts: all branches tested — PreconditionFailed match, wrong code, missing code property, null, string/number primitives, and duck-typed plain object. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { RpcError } from '../src/transport/RpcError';
|
|
import { isPreconditionFailed } from '../src/proto/rpcError';
|
|
import { ErrorCode } from '../src/proto/types';
|
|
|
|
describe('RpcError', () => {
|
|
it('sets name, code, message, and data from constructor arguments', () => {
|
|
const e = new RpcError(ErrorCode.FileNotFound, 'not found', { path: 'foo.md' });
|
|
expect(e.name).toBe('RpcError');
|
|
expect(e.code).toBe(ErrorCode.FileNotFound);
|
|
expect(e.message).toBe('not found');
|
|
expect(e.data).toEqual({ path: 'foo.md' });
|
|
});
|
|
|
|
it('data is undefined when the third argument is omitted', () => {
|
|
const e = new RpcError(ErrorCode.InternalError, 'oops');
|
|
expect(e.data).toBeUndefined();
|
|
});
|
|
|
|
it('is an instance of Error so catch blocks that check instanceof Error still catch it', () => {
|
|
expect(new RpcError(ErrorCode.InternalError, 'err')).toBeInstanceOf(Error);
|
|
});
|
|
|
|
it('is() returns true when the code matches', () => {
|
|
const e = new RpcError(ErrorCode.FileNotFound, 'not found');
|
|
expect(e.is(ErrorCode.FileNotFound)).toBe(true);
|
|
});
|
|
|
|
it('is() returns false when the code does not match', () => {
|
|
const e = new RpcError(ErrorCode.FileNotFound, 'not found');
|
|
expect(e.is(ErrorCode.PermissionDenied)).toBe(false);
|
|
});
|
|
|
|
it('is() distinguishes between close numeric codes (AuthRequired vs AuthInvalid)', () => {
|
|
const e = new RpcError(ErrorCode.AuthRequired, 'auth required');
|
|
expect(e.is(ErrorCode.AuthRequired)).toBe(true);
|
|
expect(e.is(ErrorCode.AuthInvalid)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isPreconditionFailed', () => {
|
|
it('returns true when the object has code === PreconditionFailed', () => {
|
|
expect(isPreconditionFailed({ code: ErrorCode.PreconditionFailed })).toBe(true);
|
|
});
|
|
|
|
it('returns false for a different numeric error code', () => {
|
|
expect(isPreconditionFailed({ code: ErrorCode.FileNotFound })).toBe(false);
|
|
});
|
|
|
|
it('returns false when the object has no code property', () => {
|
|
expect(isPreconditionFailed({ message: 'oops' })).toBe(false);
|
|
});
|
|
|
|
it('returns false for null', () => {
|
|
expect(isPreconditionFailed(null)).toBe(false);
|
|
});
|
|
|
|
it('returns false for a string primitive', () => {
|
|
expect(isPreconditionFailed('PreconditionFailed')).toBe(false);
|
|
});
|
|
|
|
it('returns false for a number primitive', () => {
|
|
expect(isPreconditionFailed(ErrorCode.PreconditionFailed)).toBe(false);
|
|
});
|
|
|
|
it('works with a plain object that duck-types a transport error (no RpcError import needed)', () => {
|
|
// The function is duck-typed on purpose — SFTP errors arrive as
|
|
// plain objects with a numeric `code` field, not as RpcError instances.
|
|
const sftpStyle = { code: ErrorCode.PreconditionFailed, message: 'mtime changed' };
|
|
expect(isPreconditionFailed(sftpStyle)).toBe(true);
|
|
});
|
|
});
|