071faa482c
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口 完成项目 TypeScript/Prettier 与 Rust 全量格式化 修复 Pingora expected executable 门禁的空白敏感误报 同步开发运维文档与 AGC skill pack 格式化忽略规则
119 lines
3.4 KiB
TypeScript
119 lines
3.4 KiB
TypeScript
import { describe, expect, test } from 'vitest';
|
|
|
|
import {
|
|
HOST_BRIDGE_PROTOCOL,
|
|
HOST_BRIDGE_VERSION,
|
|
type HostBridgeRequest,
|
|
} from '../../../../packages/shared/src/contracts/hostBridge';
|
|
import {
|
|
failure,
|
|
invalidRequest,
|
|
isHostBridgeRequest,
|
|
normalizeMobileHostBridgeError,
|
|
ok,
|
|
parseRequest,
|
|
unsupported,
|
|
} from './protocol';
|
|
|
|
function request(
|
|
overrides: Partial<HostBridgeRequest> = {},
|
|
): HostBridgeRequest {
|
|
return {
|
|
bridge: HOST_BRIDGE_PROTOCOL,
|
|
version: HOST_BRIDGE_VERSION,
|
|
id: 'request-1',
|
|
method: 'host.getRuntime',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('mobile HostBridge protocol helpers', () => {
|
|
test('parses JSON messages and drops malformed payloads before validation', () => {
|
|
expect(parseRequest(JSON.stringify(request()))).toEqual(request());
|
|
expect(parseRequest('{')).toBeNull();
|
|
});
|
|
|
|
test('accepts only valid HostBridge envelopes and request ids', () => {
|
|
expect(isHostBridgeRequest(request())).toBe(true);
|
|
expect(isHostBridgeRequest(request({ id: ' id-with-space ' }))).toBe(true);
|
|
|
|
for (const candidate of [
|
|
null,
|
|
{},
|
|
{ ...request(), bridge: 'wrong-bridge' },
|
|
{ ...request(), version: HOST_BRIDGE_VERSION + 1 },
|
|
request({ id: '' }),
|
|
request({ id: 'bad\u0000id' }),
|
|
request({ id: 'x'.repeat(129) }),
|
|
request({ method: 'unknown.method' as HostBridgeRequest['method'] }),
|
|
]) {
|
|
expect(isHostBridgeRequest(candidate)).toBe(false);
|
|
}
|
|
});
|
|
|
|
test('wraps successful and failed responses in the shared response envelope', () => {
|
|
expect(ok(request(), { shell: 'expo_mobile' })).toEqual({
|
|
bridge: HOST_BRIDGE_PROTOCOL,
|
|
version: HOST_BRIDGE_VERSION,
|
|
id: 'request-1',
|
|
ok: true,
|
|
result: { shell: 'expo_mobile' },
|
|
});
|
|
expect(failure(request(), invalidRequest('bad request'))).toEqual({
|
|
bridge: HOST_BRIDGE_PROTOCOL,
|
|
version: HOST_BRIDGE_VERSION,
|
|
id: 'request-1',
|
|
ok: false,
|
|
error: {
|
|
code: 'invalid_request',
|
|
message: 'bad request',
|
|
},
|
|
});
|
|
});
|
|
|
|
test('keeps unsupported and invalid request errors explicit', () => {
|
|
expect(unsupported('payment.request')).toEqual({
|
|
code: 'unsupported_method',
|
|
message: 'payment.request unsupported in mobile shell',
|
|
});
|
|
expect(invalidRequest('url is required')).toEqual({
|
|
code: 'invalid_request',
|
|
message: 'url is required',
|
|
});
|
|
});
|
|
|
|
test('passes through only allowed HostBridge errors from native helpers', () => {
|
|
expect(
|
|
normalizeMobileHostBridgeError({
|
|
code: 'cancelled',
|
|
message: 'qr scan cancelled',
|
|
nativeStack: 'hidden',
|
|
}),
|
|
).toEqual({
|
|
code: 'cancelled',
|
|
message: 'qr scan cancelled',
|
|
});
|
|
});
|
|
|
|
test('normalizes unsafe native errors to host_error without leaking fields', () => {
|
|
expect(
|
|
normalizeMobileHostBridgeError({
|
|
code: 'private_error',
|
|
message: 'do not expose',
|
|
detail: 'native secret',
|
|
}),
|
|
).toEqual({
|
|
code: 'host_error',
|
|
message: 'mobile host bridge request failed',
|
|
});
|
|
expect(normalizeMobileHostBridgeError(new Error('disk failed'))).toEqual({
|
|
code: 'host_error',
|
|
message: 'mobile host bridge request failed',
|
|
});
|
|
expect(normalizeMobileHostBridgeError('boom')).toEqual({
|
|
code: 'host_error',
|
|
message: 'mobile host bridge request failed',
|
|
});
|
|
});
|
|
});
|