import { describe, it, expect } from 'vitest'; import { canConnectRelations, canConnectDirections, checkConnection, type PortSpec, } from '../src/model/validation'; const port = (partial: Partial): PortSpec => ({ id: 'p', relation: 'water', direction: 'any', x: 0, y: 0, ...partial, }); describe('relation compatibility', () => { it('allows same-relation connections', () => { expect(canConnectRelations('water', 'water')).toBe(true); expect(canConnectRelations('gas', 'gas')).toBe(true); expect(canConnectRelations('power', 'power')).toBe(true); }); it('allows declared cross-relation pairs symmetrically', () => { expect(canConnectRelations('water', 'heat')).toBe(true); expect(canConnectRelations('heat', 'water')).toBe(true); expect(canConnectRelations('sewage', 'water')).toBe(true); }); it('rejects incompatible relations', () => { expect(canConnectRelations('water', 'power')).toBe(false); expect(canConnectRelations('gas', 'water')).toBe(false); expect(canConnectRelations('signal', 'power')).toBe(false); }); }); describe('direction compatibility', () => { it('rejects out→out and in→in', () => { expect(canConnectDirections('out', 'out')).toBe(false); expect(canConnectDirections('in', 'in')).toBe(false); }); it('allows out→in, in→out and any combinations', () => { expect(canConnectDirections('out', 'in')).toBe(true); expect(canConnectDirections('in', 'out')).toBe(true); expect(canConnectDirections('any', 'out')).toBe(true); expect(canConnectDirections('any', 'any')).toBe(true); }); }); describe('checkConnection scenarios', () => { it('pump discharge (water out) → consumer water inlet: allowed', () => { const r = checkConnection( port({ id: 'out', relation: 'water', direction: 'out' }), port({ id: 'waterIn', relation: 'water', direction: 'in' }), ); expect(r.ok).toBe(true); }); it('power supply → water port: rejected with reason', () => { const r = checkConnection( port({ id: 'out', relation: 'power', direction: 'out' }), port({ id: 'in', relation: 'water', direction: 'in' }), ); expect(r.ok).toBe(false); expect(r.reason).toMatch(/Incompatible relations/); }); it('two source outlets: rejected by direction', () => { const r = checkConnection( port({ id: 'a', relation: 'water', direction: 'out' }), port({ id: 'b', relation: 'water', direction: 'out' }), ); expect(r.ok).toBe(false); expect(r.reason).toMatch(/Incompatible directions/); }); it('same port to itself on one node: rejected', () => { const p = port({ id: 'x' }); expect(checkConnection(p, p, { sameNode: true }).ok).toBe(false); }); it('different ports of the same node may connect (self-loop)', () => { const r = checkConnection( port({ id: 'a', relation: 'heat', direction: 'out' }), port({ id: 'b', relation: 'heat', direction: 'in' }), { sameNode: true }, ); expect(r.ok).toBe(true); }); });