Spaces:
Running
Running
| import { | |
| ApiError, | |
| rateLimitLockFromError, | |
| remainingLockSeconds | |
| } from '../client/src/api.js'; | |
| import { buildLocalTargetUrl } from '../server/relay-protocol.js'; | |
| import { | |
| nextReconnectDelay, | |
| shouldResetReconnectDelay | |
| } from './relay-mac-client-reconnect.mjs'; | |
| import { fail } from './relay-smoke-env.mjs'; | |
| export function runBasicRelayAssertions() { | |
| expectInvalidForwardPathRejected(); | |
| verifyRateLimitRetryAfterHelpers(); | |
| verifyConnectorReconnectPolicy(); | |
| } | |
| function expectInvalidForwardPathRejected() { | |
| for (const path of ['//example.com/api/status', '/api/status\r\nx: y']) { | |
| try { | |
| buildLocalTargetUrl(path, 'http://127.0.0.1:3321'); | |
| fail('invalid forward path should be rejected', path); | |
| } catch (error) { | |
| if (error.status !== 400 || error.message !== 'relay_invalid_forward_path') { | |
| fail('invalid forward path should fail with relay_invalid_forward_path', error); | |
| } | |
| } | |
| } | |
| } | |
| function verifyRateLimitRetryAfterHelpers() { | |
| const error = new ApiError('请求过快,请稍后再试。', { | |
| status: 429, | |
| code: 'relay_rate_limited', | |
| retryAfter: 3 | |
| }); | |
| const lock = rateLimitLockFromError(error, 'send', 1000); | |
| if (lock?.scope !== 'send' || lock.retryAfter !== 3 || lock.untilMs !== 4000) { | |
| fail('rate limit helper should create a scoped retryAfter lock', lock); | |
| } | |
| if (remainingLockSeconds(lock, 1001) !== 3 || remainingLockSeconds(lock, 4000) !== 0) { | |
| fail('rate limit helper should expose remaining lock seconds', lock); | |
| } | |
| const ignored = rateLimitLockFromError(new ApiError('offline', { status: 503, code: 'mac_offline' }), 'send', 1000); | |
| if (ignored) { | |
| fail('non-rate-limit errors should not create operation locks', ignored); | |
| } | |
| } | |
| function verifyConnectorReconnectPolicy() { | |
| let delay = nextReconnectDelay(60000, { active: true, idleHeartbeatMs: 300000 }); | |
| if (delay.delayMs !== 30000 || delay.nextDelayMs !== 30000) { | |
| fail('active reconnect delay should cap at 30 seconds', delay); | |
| } | |
| delay = nextReconnectDelay(600000, { active: false, idleHeartbeatMs: 300000 }); | |
| if (delay.delayMs !== 300000 || delay.nextDelayMs !== 300000) { | |
| fail('idle reconnect delay should cap at 5 minutes', delay); | |
| } | |
| if (shouldResetReconnectDelay(59999) || !shouldResetReconnectDelay(60000)) { | |
| fail('stable online reconnect delay reset should require 60 seconds'); | |
| } | |
| } | |