Files
3x-ui/frontend/src/test/mtproto-egress-picker.test.tsx
T
BlindMaster24 84c5aef4a1 fix(panel): probe UDP outbounds and hide the block outbound from the mtproto egress picker (#6525)
* fix(panel): match the probe and egress readers to what the core loads

Two readers left over from the case-sensitivity sweep still disagreed with
the core, both raised reviewing #6523.

isUdpOutbound compared the protocol id and the transport name exactly. The
core lowercases both before it resolves them (infra/conf/loader.go:46 for
the id, TransportProtocol.Build at infra/conf/transport_internet.go:16-17
for the name), so an outbound spelled "WireGuard" or a stream named "KCP"
still built a UDP handler but was probed with a dial-only TCP request, and
Test All Outbounds reported a working outbound as down.

The mtproto egress picker asked for outbound tags without excludeBlackhole,
so the block outbound stayed selectable there. Choosing it looks like a
working selection and discards that inbound's Telegram traffic.

* fix(panel): recognise the mkcp transport alias and pin the picker's field id

Review findings on #6525.

TransportProtocol.Build resolves both "kcp" and "mkcp" to the same mKCP
transport, so comparing the transport name against "kcp" alone left a
template spelling "network": "mkcp" in the TCP lane and reported a working
outbound as down — the same trigger this PR already fixed for the "KCP"
capitalisation.

The egress picker now carries an explicit id, the way the inbound form's
protocol select does, so the test addresses that field rather than the first
searchable select on the page and reuses the shared dropdown helper instead
of duplicating it.
2026-09-14 16:00:13 +02:00

59 lines
1.9 KiB
TypeScript

import type { ReactNode } from 'react';
import { Form } from 'antd';
import { waitFor } from '@testing-library/react';
import { FormProvider, useForm } from 'react-hook-form';
import { afterEach, describe, expect, it, vi } from 'vitest';
import MtprotoFields from '@/pages/inbounds/form/protocols/mtproto';
import { HttpUtil, Msg } from '@/utils';
import { listSelectOptions, renderWithProviders } from './test-utils';
afterEach(() => {
vi.restoreAllMocks();
});
// The picker exists so Telegram traffic can be sent through a proxy; offering
// the block outbound there looks like a working selection and drops the traffic.
function mockConfigWithBlockOutbound() {
const payload = {
xraySetting: {
outbounds: [
{ tag: 'direct', protocol: 'freedom' },
{ tag: 'blocked', protocol: 'blackhole' },
{ tag: 'warp', protocol: 'wireguard' },
],
},
};
vi.spyOn(HttpUtil, 'post').mockResolvedValue(new Msg(true, '', JSON.stringify(payload)));
}
function Harness({ children }: { children: ReactNode }) {
const methods = useForm({ defaultValues: { settings: { routeThroughXray: true } } });
return (
<FormProvider {...methods}>
<Form>{children}</Form>
</FormProvider>
);
}
// The field carries an explicit id (like the inbound form's protocol select), so
// the assertions can't drift onto another select that happens to be nearby.
const EGRESS_FIELD = 'mtprotoOutboundTag';
describe('mtproto egress picker', () => {
it('offers the routable tags and not the block outbound', async () => {
mockConfigWithBlockOutbound();
renderWithProviders(
<Harness>
<MtprotoFields />
</Harness>,
);
await waitFor(() => expect(listSelectOptions(EGRESS_FIELD)).toContain('direct'));
const options = listSelectOptions(EGRESS_FIELD);
expect(options).toContain('warp');
expect(options).not.toContain('blocked');
});
});