Files
sencho/frontend/src/components/fleet/meshTransport.test.ts
T
Anso 81858a0bb0 feat(mesh): refine the Routing tab and add per-route removal (#1311)
* feat(mesh): refine the Routing tab and add per-route removal

Tighten the Fleet > Routing tab so its node cards and diagnostics read
honestly for proxy-connected fleets, and remove a couple of dead ends.

- Node cards drop the "pilot connected/offline" line, which was meaningless
  for nodes that connect over the HTTP API proxy. The compact card drops the
  matching agent cell and now reads stacks / aliases / bridge.
- Enabling mesh on a proxy node shows a transient "Connecting" state and
  settles to meshed on its own, instead of flashing "Degraded" with a manual
  refresh while the bridge finishes dialing.
- The alias detail sheet gains a "Remove from mesh" action that opts the
  alias's owning stack out (the confirmation says how many aliases that
  drops), and a "Topology" tab. The standalone topology sheet and its
  opt-in-sheet shortcut are removed in favor of the tab.
- The "Add stack" affordance stays reachable on a meshed node, so a node
  that already has aliases is no longer a dead end.
- Diagnostics and the alias detail sheet show a transport-aware line
  ("local", "API proxy bridge", or "Pilot tunnel" with its state) instead of
  a fixed "Pilot tunnel" label.

Adds unit coverage for the new state derivation, transport descriptor, the
enable auto-converge, and the admin-gated removal, and updates the mesh docs.

* fix(mesh): harden Routing tab toggle and alias sheet against async races

Independent review surfaced two narrow async races in the new code.

- RoutingNodeCard: cancel the converge re-poll batch at the start of any
  toggle (so a slow disable can't let a prior enable's re-polls fire), and
  guard the toast, refresh, timer scheduling, and setToggling behind a mounted
  ref so nothing runs after the card unmounts while the enable POST is still
  in flight.
- MeshRouteDetailSheet: re-check the cancelled flag after reading the
  diagnostic body, not just before it. Reading the body is itself async, so a
  superseded alias's diagnostic could otherwise call setDiag and expose Remove
  for the wrong stack.

Adds tests for unmount-before-enable-resolves and the deferred-diagnostic
alias switch.
2026-06-04 21:45:53 -04:00

61 lines
2.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { describeTransport, reverseBridgeLabel } from './meshTransport';
import type { MeshNodeStatus } from '@/types/mesh';
function node(over: Partial<MeshNodeStatus>): MeshNodeStatus {
return {
nodeId: 1,
nodeName: 'n',
enabled: true,
localForwarderListening: null,
pilotConnected: false,
reachableMode: 'local',
reachableReason: null,
reverseCallbackStatus: 'not_applicable',
optedInStacks: [],
activeStreamCount: 0,
...over,
};
}
describe('reverseBridgeLabel', () => {
it('maps every reverse-callback status', () => {
expect(reverseBridgeLabel('connected')).toBe('connected');
expect(reverseBridgeLabel('connecting')).toBe('connecting');
expect(reverseBridgeLabel('unavailable')).toBe('unavailable');
expect(reverseBridgeLabel('not_applicable')).toBe('n/a');
});
});
describe('describeTransport', () => {
it('returns an unknown line for a missing node rather than a false positive', () => {
expect(describeTransport(undefined, true)).toEqual({ label: 'Transport', value: 'unknown' });
});
it('labels a local node as in-process', () => {
expect(describeTransport(node({ reachableMode: 'local' }), false))
.toEqual({ label: 'Transport', value: 'local (in-process)' });
});
it('still labels a pilot node with its tunnel state', () => {
expect(describeTransport(node({ reachableMode: 'pilot' }), true))
.toEqual({ label: 'Pilot tunnel', value: 'connected' });
expect(describeTransport(node({ reachableMode: 'pilot' }), false))
.toEqual({ label: 'Pilot tunnel', value: 'disconnected' });
});
it('labels a proxy node with its API proxy bridge state, never a pilot tunnel', () => {
expect(describeTransport(node({ reachableMode: 'proxy', reverseCallbackStatus: 'connected' }), false))
.toEqual({ label: 'API proxy bridge', value: 'connected' });
expect(describeTransport(node({ reachableMode: 'proxy', reverseCallbackStatus: 'connecting' }), false).value)
.toBe('connecting');
expect(describeTransport(node({ reachableMode: 'proxy', reverseCallbackStatus: 'unavailable' }), false).value)
.toBe('unavailable');
});
it('labels an unreachable node', () => {
expect(describeTransport(node({ reachableMode: 'unreachable' }), false))
.toEqual({ label: 'Transport', value: 'unreachable' });
});
});