mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-27 12:18:59 +00:00
fix(mesh): reject opt-in when every service has empty ports (#1056)
optInStack rejected empty service lists but not the case where services exist with every ports: []. The newPorts Set ended up empty, the function sailed past collision checks, wrote a mesh_stacks row, and produced an empty-aliases override. The stack then appeared online in the Routing tab but no traffic actually routed. Add a check after building newPorts: throw no_target with a clear message before the SENCHO_LISTEN_PORT collision check. Reuses the existing MeshError code (semantically: no routable target) so the frontend's error toast path is unchanged.
This commit is contained in:
@@ -70,6 +70,26 @@ describe('MeshService.optInStack', () => {
|
||||
.rejects.toThrow(/port 5432 is already claimed/);
|
||||
});
|
||||
|
||||
it('rejects opt-in when every service declared empty ports (no routable target)', async () => {
|
||||
const svc = MeshService.getInstance();
|
||||
const db = DatabaseService.getInstance();
|
||||
const localNodeId = db.getNodes()[0].id;
|
||||
|
||||
vi.spyOn(svc as unknown as { inspectStackServices: (n: number, s: string) => Promise<unknown> }, 'inspectStackServices')
|
||||
.mockResolvedValue([
|
||||
{ service: 'web', ports: [] },
|
||||
{ service: 'db', ports: [] },
|
||||
]);
|
||||
vi.spyOn(svc as unknown as { regenerateOverridesForNode: (n: number) => Promise<void> }, 'regenerateOverridesForNode')
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
await expect(svc.optInStack(localNodeId, 'silent', 'tester'))
|
||||
.rejects.toThrow(/no service ports to mesh/);
|
||||
// Pre-fix this wrote a row with no aliases and the stack appeared
|
||||
// "online but doesn't route". Verify the row was not written.
|
||||
expect(db.isMeshStackEnabled(localNodeId, 'silent')).toBe(false);
|
||||
});
|
||||
|
||||
it('opt-out removes the row and the override', async () => {
|
||||
const svc = MeshService.getInstance();
|
||||
vi.spyOn(svc as unknown as { inspectStackServices: (n: number, s: string) => Promise<unknown> }, 'inspectStackServices')
|
||||
|
||||
@@ -565,6 +565,12 @@ export class MeshService extends EventEmitter implements MeshForwarderHost {
|
||||
|
||||
const newPorts = new Set<number>();
|
||||
for (const svc of services) for (const p of svc.ports) newPorts.add(p);
|
||||
if (newPorts.size === 0) {
|
||||
throw new MeshError(
|
||||
'no_target',
|
||||
`stack ${stackName} has no service ports to mesh (every service declared ports: [])`,
|
||||
);
|
||||
}
|
||||
if (newPorts.has(SENCHO_LISTEN_PORT)) {
|
||||
throw new MeshError(
|
||||
'port_collision',
|
||||
|
||||
Reference in New Issue
Block a user