feat(editor): F4.5b the device sheet for routers and home routers
Measured first: SheetRun now types into FILIUS' router and home router
panels, records five forwarding tables (holeTabelle) and steps in the
panel's table. Java turns a card address that passes the pattern but not
IP.ipCheck (".0.0.1") into null and the router panel throws; an empty
gateway is marked red yet taken; a refused table cell is stored as null;
a home router's DHCP client leaves its DHCP server running. None of the
nulls is adopted. The core's routeTableRows matches all five tables.
- doc: card addresses (the first card mirrored in the device's settings),
router gateway, RIP, manual routes (a new row once all four cells pass,
a cell, removal); the sheet reads cards with what their cable joins and
the whole table
- editor: Java's tabs (general, each card titled by its address, the
forwarding table greyed out under RIP; general, LAN, WAN on a home
router); the open card's cable drawn active in Java's green
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,3 +78,41 @@ modem port
|
||||
modem port 080
|
||||
modem port \u002080
|
||||
modem port 80
|
||||
router name R1
|
||||
router name
|
||||
router ip 10.0.0.1
|
||||
router ip 010.000.000.001
|
||||
router ip 10..0.0.1
|
||||
router ip .0.0.1
|
||||
router ip +10.0.0.1
|
||||
router ip 256.0.0.1
|
||||
router ip 10.0.0.1
|
||||
router ip 10.0.0.1
|
||||
router ip
|
||||
router ip 10.0.0.1.
|
||||
router mask 255.255.0.0
|
||||
router mask 255.0.255.0
|
||||
router mask 256.0.0.0
|
||||
router mask 255..255.0.0
|
||||
router mask
|
||||
router gateway
|
||||
router gateway 10.0.0.254
|
||||
router gateway
|
||||
router gateway abc
|
||||
router gateway 192.168..0.1
|
||||
router gateway 10.0.0.256
|
||||
homeRouter name Heim
|
||||
homeRouter gateway
|
||||
homeRouter gateway 42.0.0.1
|
||||
homeRouter gateway 42.0.0
|
||||
homeRouter wanIp 42.1.2.3
|
||||
homeRouter wanIp .1.2.3
|
||||
homeRouter wanIp 42..1.2
|
||||
homeRouter wanIp 042.1.2.3
|
||||
homeRouter wanMask 255.255.0.0
|
||||
homeRouter wanMask 255.255
|
||||
homeRouter lanIp 192.168.1.1
|
||||
homeRouter lanIp 192.168.1.256
|
||||
homeRouter lanIp 192.168.1.1
|
||||
homeRouter lanMask 255.255.255.128
|
||||
homeRouter lanMask abc
|
||||
|
||||
@@ -692,12 +692,804 @@
|
||||
"marked": false,
|
||||
"stored": "80",
|
||||
"shown": "80"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "name",
|
||||
"input": "R1",
|
||||
"start": "Vermittlungsrechner",
|
||||
"marked": false,
|
||||
"stored": "R1",
|
||||
"shown": "R1"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "name",
|
||||
"input": "",
|
||||
"start": "Vermittlungsrechner",
|
||||
"marked": false,
|
||||
"stored": "",
|
||||
"shown": ""
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "10.0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": false,
|
||||
"stored": "10.0.0.1",
|
||||
"shown": "10.0.0.1"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "010.000.000.001",
|
||||
"start": "192.168.0.10",
|
||||
"marked": false,
|
||||
"stored": "10.0.0.1",
|
||||
"shown": "10.0.0.1"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "10..0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": ".0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": false,
|
||||
"stored": null,
|
||||
"shown": "",
|
||||
"error": "NullPointerException"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "+10.0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "256.0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": " 10.0.0.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "10.0.0.1 ",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "ip",
|
||||
"input": "10.0.0.1.",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "mask",
|
||||
"input": "255.255.0.0",
|
||||
"start": "255.255.255.0",
|
||||
"marked": false,
|
||||
"stored": "255.255.0.0",
|
||||
"shown": "255.255.0.0"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "mask",
|
||||
"input": "255.0.255.0",
|
||||
"start": "255.255.255.0",
|
||||
"marked": false,
|
||||
"stored": "255.0.255.0",
|
||||
"shown": "255.0.255.0"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "mask",
|
||||
"input": "256.0.0.0",
|
||||
"start": "255.255.255.0",
|
||||
"marked": true,
|
||||
"stored": "255.255.255.0",
|
||||
"shown": "255.255.255.0"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "mask",
|
||||
"input": "255..255.0.0",
|
||||
"start": "255.255.255.0",
|
||||
"marked": true,
|
||||
"stored": "255.255.255.0",
|
||||
"shown": "255.255.255.0"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "mask",
|
||||
"input": "",
|
||||
"start": "255.255.255.0",
|
||||
"marked": true,
|
||||
"stored": "255.255.255.0",
|
||||
"shown": "255.255.255.0"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": "",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "",
|
||||
"shown": ""
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": "10.0.0.254",
|
||||
"start": "192.168.0.254",
|
||||
"marked": false,
|
||||
"stored": "10.0.0.254",
|
||||
"shown": "10.0.0.254"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": " ",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "",
|
||||
"shown": ""
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": "abc",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.254",
|
||||
"shown": "192.168.0.254"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": "192.168..0.1",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.1",
|
||||
"shown": "192.168.0.1"
|
||||
},
|
||||
{
|
||||
"device": "router",
|
||||
"field": "gateway",
|
||||
"input": "10.0.0.256",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.254",
|
||||
"shown": "192.168.0.254"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "name",
|
||||
"input": "Heim",
|
||||
"start": "Heimrouter",
|
||||
"marked": false,
|
||||
"stored": "Heim",
|
||||
"shown": "Heim"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "gateway",
|
||||
"input": "",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "",
|
||||
"shown": ""
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "gateway",
|
||||
"input": "42.0.0.1",
|
||||
"start": "192.168.0.254",
|
||||
"marked": false,
|
||||
"stored": "42.0.0.1",
|
||||
"shown": "42.0.0.1"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "gateway",
|
||||
"input": "42.0.0",
|
||||
"start": "192.168.0.254",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.254",
|
||||
"shown": "192.168.0.254"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanIp",
|
||||
"input": "42.1.2.3",
|
||||
"start": "42.0.0.10",
|
||||
"marked": false,
|
||||
"stored": "42.1.2.3",
|
||||
"shown": "42.1.2.3"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanIp",
|
||||
"input": ".1.2.3",
|
||||
"start": "42.0.0.10",
|
||||
"marked": false,
|
||||
"stored": null,
|
||||
"shown": ""
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanIp",
|
||||
"input": "42..1.2",
|
||||
"start": "42.0.0.10",
|
||||
"marked": true,
|
||||
"stored": "42.0.0.10",
|
||||
"shown": "42.0.0.10"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanIp",
|
||||
"input": "042.1.2.3",
|
||||
"start": "42.0.0.10",
|
||||
"marked": false,
|
||||
"stored": "42.1.2.3",
|
||||
"shown": "42.1.2.3"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanMask",
|
||||
"input": "255.255.0.0",
|
||||
"start": "255.0.0.0",
|
||||
"marked": false,
|
||||
"stored": "255.255.0.0",
|
||||
"shown": "255.255.0.0"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "wanMask",
|
||||
"input": "255.255",
|
||||
"start": "255.0.0.0",
|
||||
"marked": true,
|
||||
"stored": "255.0.0.0",
|
||||
"shown": "255.0.0.0"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "lanIp",
|
||||
"input": "192.168.1.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": false,
|
||||
"stored": "192.168.1.1",
|
||||
"shown": "192.168.1.1"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "lanIp",
|
||||
"input": "192.168.1.256",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "lanIp",
|
||||
"input": " 192.168.1.1",
|
||||
"start": "192.168.0.10",
|
||||
"marked": true,
|
||||
"stored": "192.168.0.10",
|
||||
"shown": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "lanMask",
|
||||
"input": "255.255.255.128",
|
||||
"start": "255.255.255.0",
|
||||
"marked": false,
|
||||
"stored": "255.255.255.128",
|
||||
"shown": "255.255.255.128"
|
||||
},
|
||||
{
|
||||
"device": "homeRouter",
|
||||
"field": "lanMask",
|
||||
"input": "abc",
|
||||
"start": "255.255.255.0",
|
||||
"marked": true,
|
||||
"stored": "255.255.255.0",
|
||||
"shown": "255.255.255.0"
|
||||
}
|
||||
],
|
||||
"flows": {
|
||||
"useMacAsNameThenOff": "0E:EE:AA:A5:58:21",
|
||||
"useMacAsNameThenOff": "CE:0D:5A:24:8A:C6",
|
||||
"useIpAsNameThenOff": "PC-A",
|
||||
"dhcpClientStopsServer": true,
|
||||
"dhcpClient": true
|
||||
"dhcpClient": true,
|
||||
"homeRouterDhcpClientStopsServer": false,
|
||||
"homeRouterDhcpClient": true
|
||||
},
|
||||
"tables": {
|
||||
"gatewayInSecondNetwork": [
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.0",
|
||||
"255.255.255.0",
|
||||
"192.168.1.1",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.0",
|
||||
"255.0.0.0",
|
||||
"10.0.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"127.0.0.0",
|
||||
"255.0.0.0",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.1.2",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": true
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.20.0.0",
|
||||
"255.255.0.0",
|
||||
"10.0.0.2",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": true
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"0.0.0.0",
|
||||
"0.0.0.0",
|
||||
"192.168.1.254",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": false
|
||||
}
|
||||
],
|
||||
"gatewayInNoNetwork": [
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.0",
|
||||
"255.255.255.0",
|
||||
"192.168.1.1",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.0",
|
||||
"255.0.0.0",
|
||||
"10.0.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"127.0.0.0",
|
||||
"255.0.0.0",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.1.2",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": true
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.20.0.0",
|
||||
"255.255.0.0",
|
||||
"10.0.0.2",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": true
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"0.0.0.0",
|
||||
"0.0.0.0",
|
||||
"172.30.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
}
|
||||
],
|
||||
"gatewayInBothNetworks": [
|
||||
{
|
||||
"cells": [
|
||||
"10.1.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.1.0.0",
|
||||
"255.255.0.0",
|
||||
"10.1.0.1",
|
||||
"10.1.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.0",
|
||||
"255.0.0.0",
|
||||
"10.0.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"127.0.0.0",
|
||||
"255.0.0.0",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"0.0.0.0",
|
||||
"0.0.0.0",
|
||||
"10.1.0.254",
|
||||
"10.1.0.1"
|
||||
],
|
||||
"manual": false
|
||||
}
|
||||
],
|
||||
"noGateway": [
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.0",
|
||||
"255.255.255.0",
|
||||
"192.168.1.1",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.0",
|
||||
"255.0.0.0",
|
||||
"10.0.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"127.0.0.0",
|
||||
"255.0.0.0",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.1.2",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": true
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.20.0.0",
|
||||
"255.255.0.0",
|
||||
"10.0.0.2",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": true
|
||||
}
|
||||
],
|
||||
"threeInterfaces": [
|
||||
{
|
||||
"cells": [
|
||||
"172.16.5.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.1",
|
||||
"255.255.255.255",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"172.16.5.1",
|
||||
"172.16.5.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"192.168.1.0",
|
||||
"255.255.255.0",
|
||||
"192.168.1.1",
|
||||
"192.168.1.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"10.0.0.0",
|
||||
"255.0.0.0",
|
||||
"10.0.0.1",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"127.0.0.0",
|
||||
"255.0.0.0",
|
||||
"127.0.0.1",
|
||||
"127.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
},
|
||||
{
|
||||
"cells": [
|
||||
"0.0.0.0",
|
||||
"0.0.0.0",
|
||||
"10.0.0.254",
|
||||
"10.0.0.1"
|
||||
],
|
||||
"manual": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"edits": {
|
||||
"invalidCell": {
|
||||
"manual": [
|
||||
[
|
||||
null,
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"emptyCell": {
|
||||
"manual": [
|
||||
[
|
||||
null,
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"leadingZeros": {
|
||||
"manual": [
|
||||
[
|
||||
"10.0.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"dottedTwice": {
|
||||
"manual": [
|
||||
[
|
||||
null,
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"maskCell": {
|
||||
"manual": [
|
||||
[
|
||||
"172.16.0.0",
|
||||
"255.0.255.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"newRowThreeCells": {
|
||||
"manual": [
|
||||
[
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"newRowFourCells": {
|
||||
"manual": [
|
||||
[
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
],
|
||||
[
|
||||
"10.9.0.0",
|
||||
"255.255.0.0",
|
||||
"10.0.0.9",
|
||||
"10.0.0.1"
|
||||
]
|
||||
]
|
||||
},
|
||||
"newRowInvalidCell": {
|
||||
"manual": [
|
||||
[
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
]
|
||||
},
|
||||
"newRowTwice": {
|
||||
"manual": [
|
||||
[
|
||||
"172.16.0.0",
|
||||
"255.255.0.0",
|
||||
"192.168.0.2",
|
||||
"192.168.0.10"
|
||||
]
|
||||
],
|
||||
"rowsShown": 2
|
||||
},
|
||||
"removeRow": {
|
||||
"manual": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+322
-29
@@ -28,25 +28,50 @@ import {
|
||||
setSwitchCloud,
|
||||
setSwitchRetention,
|
||||
setSwitchSsid,
|
||||
addRoute,
|
||||
removeRoute,
|
||||
setInterfaceAddress,
|
||||
setRip,
|
||||
setRouteCell,
|
||||
setRouterGateway,
|
||||
topologyView,
|
||||
type FieldRule,
|
||||
type HomeRouterSheet,
|
||||
type HostSheet,
|
||||
type NewDevice,
|
||||
type RouterSheet,
|
||||
} from '../src/index.js';
|
||||
|
||||
interface JavaCase {
|
||||
readonly device: 'host' | 'switch' | 'modem';
|
||||
readonly device: 'host' | 'switch' | 'modem' | 'router' | 'homeRouter';
|
||||
readonly field: string;
|
||||
readonly input: string;
|
||||
readonly start: string;
|
||||
readonly marked: boolean;
|
||||
readonly stored: string;
|
||||
/** null: Java's card lost its address. */
|
||||
readonly stored: string | null;
|
||||
readonly shown: string;
|
||||
/** What Java's panel threw, if it did. */
|
||||
readonly error?: string;
|
||||
}
|
||||
|
||||
interface JavaRow {
|
||||
readonly cells: readonly (string | null)[];
|
||||
readonly manual: boolean;
|
||||
}
|
||||
|
||||
const java = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'sheet.java.json'), 'utf8')) as {
|
||||
readonly cases: readonly JavaCase[];
|
||||
readonly flows: { readonly useMacAsNameThenOff: string; readonly useIpAsNameThenOff: string; readonly dhcpClientStopsServer: boolean; readonly dhcpClient: boolean };
|
||||
readonly flows: {
|
||||
readonly useMacAsNameThenOff: string;
|
||||
readonly useIpAsNameThenOff: string;
|
||||
readonly dhcpClientStopsServer: boolean;
|
||||
readonly dhcpClient: boolean;
|
||||
readonly homeRouterDhcpClientStopsServer: boolean;
|
||||
readonly homeRouterDhcpClient: boolean;
|
||||
};
|
||||
readonly tables: Readonly<Record<string, readonly JavaRow[]>>;
|
||||
readonly edits: Readonly<Record<string, { readonly manual: readonly (readonly (string | null)[])[]; readonly rowsShown?: number }>>;
|
||||
};
|
||||
|
||||
const RULES: Readonly<Record<string, FieldRule>> = {
|
||||
@@ -60,10 +85,27 @@ const RULES: Readonly<Record<string, FieldRule>> = {
|
||||
'switch retention': 'seconds',
|
||||
'modem name': 'name',
|
||||
'modem port': 'port',
|
||||
'router name': 'name',
|
||||
'router ip': 'ipAddress',
|
||||
'router mask': 'subnetMask',
|
||||
'router gateway': 'ipAddressOrEmpty',
|
||||
'homeRouter name': 'name',
|
||||
'homeRouter gateway': 'ipAddressOrEmpty',
|
||||
'homeRouter wanIp': 'ipAddress',
|
||||
'homeRouter wanMask': 'subnetMask',
|
||||
'homeRouter lanIp': 'ipAddress',
|
||||
'homeRouter lanMask': 'subnetMask',
|
||||
};
|
||||
|
||||
/** Fields whose text is judged without the spaces around it. */
|
||||
const TRIMMED = new Set(['host ip', 'host mask', 'host gateway', 'host dns', 'switch retention', 'modem port']);
|
||||
const TRIMMED = new Set(
|
||||
Object.entries(RULES)
|
||||
.filter(([key, rule]) => rule !== 'name' && rule !== 'ssid')
|
||||
.map(([key]) => key),
|
||||
);
|
||||
|
||||
/** Whether Java's device took the text: its value changed (to an address: null is a card that lost its own), or was the text already. */
|
||||
const tookIt = (each: JavaCase) => each.stored !== null && (each.stored !== each.start || each.input === each.start);
|
||||
|
||||
/** What Java's model holds in the units of the sheet's value: seconds for the retention time. */
|
||||
const javaValue = (each: JavaCase) => (each.field === 'retention' ? String(Number(each.stored) / 1000) : each.stored);
|
||||
@@ -88,9 +130,12 @@ describe("a field's rule, against FILIUS' settings panels", () => {
|
||||
const key = `${each.device} ${each.field}`;
|
||||
const verdict = judged(each);
|
||||
// Taken: the value changed, or the text was the value already.
|
||||
const took = verdict.stored !== verdict.start || verdict.input === verdict.start;
|
||||
const took = tookIt(verdict);
|
||||
// An empty gateway Java's setter takes as "none", though a router's panel marks it (its key listener checks musterIpAdresse).
|
||||
const emptyTaken = RULES[key] === 'ipAddressOrEmpty' && verdict.input.trim() === '' && took;
|
||||
// A name is taken as typed. A modem's port Java never marks: it takes any int; here Java's own rule for ports.
|
||||
const expected = RULES[key] === 'name' ? true : key === 'modem port' ? took && /^([1-9][0-9]{0,4})$/.test(verdict.input) && Number(verdict.input) <= 65_535 : !verdict.marked && took;
|
||||
const expected =
|
||||
RULES[key] === 'name' ? true : key === 'modem port' ? took && /^([1-9][0-9]{0,4})$/.test(verdict.input) && Number(verdict.input) <= 65_535 : emptyTaken || (!verdict.marked && took);
|
||||
const checked = checkField(RULES[key]!, each.input);
|
||||
expect('value' in checked, `${key} ${JSON.stringify(each.input)}`).toBe(expected);
|
||||
if ('value' in checked) expect(checked.value, `${key} ${JSON.stringify(each.input)}`).toBe(RULES[key] === 'name' ? each.input : javaValue(verdict));
|
||||
@@ -98,39 +143,46 @@ describe("a field's rule, against FILIUS' settings panels", () => {
|
||||
});
|
||||
|
||||
it("names where Java's two judgements disagree, which here are one", () => {
|
||||
const disagreeing = java.cases.filter((each) => RULES[`${each.device} ${each.field}`] !== 'name' && each.field !== 'port' && each.marked === (each.stored !== each.start));
|
||||
// Marked red, yet taken; or black, yet refused (the gateway's spaces Java trims to empty).
|
||||
expect(disagreeing.map((each) => `${each.field} ${JSON.stringify(each.input)} ${each.marked ? 'marked, taken' : 'refused'}`)).toEqual([
|
||||
'ip "10..0.0.1" marked, taken',
|
||||
'ip ".0.0.1" refused',
|
||||
'ip "+10.0.0.1" marked, taken',
|
||||
'ip "10.0.0.1." marked, taken',
|
||||
'ip "١٠.0.0.1" marked, taken',
|
||||
'ip "-0.0.0.1" marked, taken',
|
||||
'mask "255..255.0.0" marked, taken',
|
||||
'gateway " " marked, taken',
|
||||
'gateway "192.168..1.1" marked, taken',
|
||||
'dns " " marked, taken',
|
||||
'retention "" refused',
|
||||
'retention "-5" marked, taken',
|
||||
'retention "99999999999999999999" refused',
|
||||
'retention "٣" marked, taken',
|
||||
const disagreeing = java.cases.filter((each) => RULES[`${each.device} ${each.field}`] !== 'name' && each.field !== 'port' && each.marked === tookIt(each));
|
||||
// Marked red, yet taken; or black, yet refused (the gateway's spaces Java trims to empty) — or lost: a card without its address.
|
||||
const verdictOf = (each: JavaCase) => (each.marked ? 'marked, taken' : each.stored === null ? `lost${each.error === undefined ? '' : `, ${each.error}`}` : 'refused');
|
||||
expect(disagreeing.map((each) => `${each.device} ${each.field} ${JSON.stringify(each.input)} ${verdictOf(each)}`)).toEqual([
|
||||
'host ip "10..0.0.1" marked, taken',
|
||||
'host ip ".0.0.1" refused',
|
||||
'host ip "+10.0.0.1" marked, taken',
|
||||
'host ip "10.0.0.1." marked, taken',
|
||||
'host ip "١٠.0.0.1" marked, taken',
|
||||
'host ip "-0.0.0.1" marked, taken',
|
||||
'host mask "255..255.0.0" marked, taken',
|
||||
'host gateway " " marked, taken',
|
||||
'host gateway "192.168..1.1" marked, taken',
|
||||
'host dns " " marked, taken',
|
||||
'switch retention "" refused',
|
||||
'switch retention "-5" marked, taken',
|
||||
'switch retention "99999999999999999999" refused',
|
||||
'switch retention "٣" marked, taken',
|
||||
'router ip ".0.0.1" lost, NullPointerException',
|
||||
'router gateway "" marked, taken',
|
||||
'router gateway " " marked, taken',
|
||||
'router gateway "192.168..0.1" marked, taken',
|
||||
'homeRouter gateway "" marked, taken',
|
||||
'homeRouter wanIp ".1.2.3" lost',
|
||||
]);
|
||||
const ports = java.cases.filter((each) => each.field === 'port' && each.stored !== each.start);
|
||||
expect(ports.map((each) => each.input)).toEqual(['0', '65535', '65536', '-1', '080', '80']);
|
||||
});
|
||||
|
||||
it("shows what the device holds once a field is left, as Java's field does", () => {
|
||||
for (const each of java.cases) if (each.field !== 'retention') expect(each.shown, each.input).toBe(each.stored);
|
||||
for (const each of java.cases) if (each.field !== 'retention') expect(each.shown, each.input).toBe(each.stored ?? '');
|
||||
});
|
||||
});
|
||||
|
||||
let ids = 0;
|
||||
const newId = () => `20000000-0000-0000-0000-${(++ids).toString(16).padStart(12, '0')}`;
|
||||
|
||||
function add(doc: Y.Doc, kind: NewDevice['kind'], name: string = kind): string {
|
||||
function add(doc: Y.Doc, kind: NewDevice['kind'], name: string = kind, interfaces?: number): string {
|
||||
const id = newId();
|
||||
addDevice(doc, { kind, id, name, x: 100, y: 100, newId }, 'me');
|
||||
addDevice(doc, { kind, id, name, x: 100, y: 100, newId, ...(interfaces === undefined ? {} : { interfaces }) }, 'me');
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -345,7 +397,7 @@ describe("a switch's and a modem's sheet", () => {
|
||||
expect(deviceSheet(doc, hub)).toMatchObject({ retentionSeconds: 1 });
|
||||
});
|
||||
|
||||
it("pairs a modem by its port, and names the other kinds' sheets", () => {
|
||||
it('pairs a modem by its port, and renames a router', () => {
|
||||
const doc = new Y.Doc();
|
||||
const modem = add(doc, 'remoteLink', 'Modem');
|
||||
expect(deviceSheet(doc, modem)).toEqual({ kind: 'remoteLink', name: 'Modem', port: 12_345 });
|
||||
@@ -355,8 +407,8 @@ describe("a switch's and a modem's sheet", () => {
|
||||
expect(deviceSheet(doc, modem)).toMatchObject({ port: 80 });
|
||||
const router = add(doc, 'router', 'R1');
|
||||
renameDevice(doc, router, 'R2', 'me');
|
||||
expect(deviceSheet(doc, router)).toEqual({ kind: 'router', name: 'R2' });
|
||||
expect(deviceSheet(doc, add(doc, 'gateway', 'Heim'))).toEqual({ kind: 'gateway', name: 'Heim' });
|
||||
expect(deviceSheet(doc, router)).toMatchObject({ kind: 'router', name: 'R2' });
|
||||
expect(deviceSheet(doc, add(doc, 'gateway', 'Heim'))).toMatchObject({ kind: 'gateway', name: 'Heim' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -413,3 +465,244 @@ describe("the sheet's changes", () => {
|
||||
expect(reopened.remoteLinkConfigurations.get(modemAgain!.id)?.pairIdentifier).toBe('java-modem-port-4242');
|
||||
});
|
||||
});
|
||||
|
||||
const router = (doc: Y.Doc, id: string) => deviceSheet(doc, id) as RouterSheet;
|
||||
const home = (doc: Y.Doc, id: string) => deviceSheet(doc, id) as HomeRouterSheet;
|
||||
|
||||
/** Java's manual rows of the measured tables, as SheetRun added them. */
|
||||
const JAVA_MANUAL = [
|
||||
{ destinationNetwork: '172.16.0.0', subnetMask: '255.255.0.0', gateway: '192.168.1.2', interfaceIPAddress: '192.168.1.1' },
|
||||
{ destinationNetwork: '010.020.0.0', subnetMask: '255.255.0.0', gateway: '10.0.0.2', interfaceIPAddress: '10.0.0.1' },
|
||||
];
|
||||
|
||||
/** The measured tables' routers: their cards (address, mask), gateway and manual rows. */
|
||||
const TABLES: Readonly<Record<string, { readonly cards: readonly (readonly [string, string])[]; readonly gateway: string; readonly manual: boolean }>> = {
|
||||
gatewayInSecondNetwork: {
|
||||
cards: [
|
||||
['10.0.0.1', '255.0.0.0'],
|
||||
['192.168.1.1', '255.255.255.0'],
|
||||
],
|
||||
gateway: '192.168.1.254',
|
||||
manual: true,
|
||||
},
|
||||
gatewayInNoNetwork: {
|
||||
cards: [
|
||||
['10.0.0.1', '255.0.0.0'],
|
||||
['192.168.1.1', '255.255.255.0'],
|
||||
],
|
||||
gateway: '172.30.0.1',
|
||||
manual: true,
|
||||
},
|
||||
gatewayInBothNetworks: {
|
||||
cards: [
|
||||
['10.0.0.1', '255.0.0.0'],
|
||||
['10.1.0.1', '255.255.0.0'],
|
||||
],
|
||||
gateway: '10.1.0.254',
|
||||
manual: false,
|
||||
},
|
||||
noGateway: {
|
||||
cards: [
|
||||
['10.0.0.1', '255.0.0.0'],
|
||||
['192.168.1.1', '255.255.255.0'],
|
||||
],
|
||||
gateway: '',
|
||||
manual: true,
|
||||
},
|
||||
threeInterfaces: {
|
||||
cards: [
|
||||
['10.0.0.1', '255.0.0.0'],
|
||||
['192.168.1.1', '255.255.255.0'],
|
||||
['172.16.5.1', '255.255.0.0'],
|
||||
],
|
||||
gateway: '10.0.0.254',
|
||||
manual: false,
|
||||
},
|
||||
};
|
||||
|
||||
describe("a router's sheet", () => {
|
||||
it("shows the forwarding table Java shows, in Java's order, its manual rows editable", () => {
|
||||
expect(Object.keys(java.tables).sort()).toEqual(Object.keys(TABLES).sort());
|
||||
for (const [name, setup] of Object.entries(TABLES)) {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router', 'R', setup.cards.length);
|
||||
router(doc, id).interfaces.forEach((card, index) => {
|
||||
expect(setInterfaceAddress(doc, id, card.portId, 'ipAddress', setup.cards[index]![0], 'me')).toEqual({ value: setup.cards[index]![0] });
|
||||
expect(setInterfaceAddress(doc, id, card.portId, 'subnetMask', setup.cards[index]![1], 'me')).toEqual({ value: setup.cards[index]![1] });
|
||||
});
|
||||
setRouterGateway(doc, id, setup.gateway, 'me');
|
||||
if (setup.manual) for (const row of JAVA_MANUAL) expect('route' in addRoute(doc, id, row, 'me')).toBe(true);
|
||||
const table = router(doc, id).table.map((row) => ({ cells: [row.destinationNetwork, row.subnetMask, row.nextHop, row.interfaceIPAddress], manual: row.isEditable }));
|
||||
expect(table, name).toEqual(java.tables[name]);
|
||||
}
|
||||
});
|
||||
|
||||
it('takes a card’s address as Java keeps it and keeps the old one for a refused text, the first card mirrored in the device', () => {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router');
|
||||
const [first, second] = router(doc, id).interfaces;
|
||||
expect(setInterfaceAddress(doc, id, first!.portId, 'ipAddress', ' 010.0.0.1 ', 'me')).toEqual({ value: '10.0.0.1' });
|
||||
expect(setInterfaceAddress(doc, id, first!.portId, 'ipAddress', '.0.0.1', 'me')).toEqual({ invalid: true });
|
||||
expect(setInterfaceAddress(doc, id, second!.portId, 'subnetMask', '255.255', 'me')).toEqual({ invalid: true });
|
||||
expect(setInterfaceAddress(doc, id, second!.portId, 'subnetMask', '255.0.255.0', 'me')).toEqual({ value: '255.0.255.0' });
|
||||
expect(setInterfaceAddress(doc, id, 'no-port', 'ipAddress', '10.0.0.1', 'me')).toEqual({ value: '10.0.0.1' });
|
||||
expect(router(doc, id).interfaces.map((card) => [card.ipAddress, card.subnetMask])).toEqual([
|
||||
['10.0.0.1', '255.255.255.0'],
|
||||
['192.168.0.10', '255.0.255.0'],
|
||||
]);
|
||||
// A router keeps its own settings only with a gateway; then they follow its first card.
|
||||
expect(readProject(doc).deviceConfigurations.has(id)).toBe(false);
|
||||
expect(setRouterGateway(doc, id, '10.0.0.254', 'me')).toEqual({ value: '10.0.0.254' });
|
||||
expect(readProject(doc).deviceConfigurations.get(id)).toEqual({ ipAddress: '10.0.0.1', subnetMask: '255.255.255.0', defaultGateway: '10.0.0.254', dnsServer: '' });
|
||||
setInterfaceAddress(doc, id, first!.portId, 'ipAddress', '10.0.0.2', 'me');
|
||||
setInterfaceAddress(doc, id, first!.portId, 'subnetMask', '255.0.0.0', 'me');
|
||||
expect(readProject(doc).deviceConfigurations.get(id)).toMatchObject({ ipAddress: '10.0.0.2', subnetMask: '255.0.0.0' });
|
||||
expect(setRouterGateway(doc, id, 'abc', 'me')).toEqual({ invalid: true });
|
||||
expect(router(doc, id).gateway).toBe('10.0.0.254');
|
||||
expect(setRouterGateway(doc, id, ' ', 'me')).toEqual({ value: '' });
|
||||
expect(readProject(doc).deviceConfigurations.has(id)).toBe(false);
|
||||
});
|
||||
|
||||
it('switches RIP, and shows IP forwarding on', () => {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router');
|
||||
expect(router(doc, id)).toMatchObject({ rip: false, ipForwarding: true, gateway: '' });
|
||||
setRip(doc, id, true, 'me');
|
||||
expect(router(doc, id).rip).toBe(true);
|
||||
expect(readProject(doc).ripEnabledByNodeId.get(id)).toBe(true);
|
||||
setRip(doc, id, false, 'me');
|
||||
expect(readProject(doc).ripEnabledByNodeId.has(id)).toBe(false);
|
||||
});
|
||||
|
||||
it('names what each card is plugged into, by its label', () => {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router');
|
||||
const pc = add(doc, 'pc', 'PC-A');
|
||||
setHostLabels(doc, pc, { ipAddress: true, macAddress: false }, 'me');
|
||||
connect(doc, { node: pc }, { node: id }, 'cable', 'me');
|
||||
expect(router(doc, id).interfaces.map((card) => card.connected)).toEqual([{ device: '192.168.0.10', link: 'cable' }, null]);
|
||||
expect(router(doc, id).interfaces[0]!.macAddress).toMatch(/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("a router's manual rows", () => {
|
||||
/** A router with Java's one manual row of SheetRun's edits. */
|
||||
function edited() {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router');
|
||||
addRoute(doc, id, { destinationNetwork: '172.16.0.0', subnetMask: '255.255.0.0', gateway: '192.168.0.2', interfaceIPAddress: '192.168.0.10' }, 'me');
|
||||
const rows = () =>
|
||||
readProject(doc)
|
||||
.manualRoutesByNodeId.get(id)
|
||||
?.map((row) => [row.destinationNetwork, row.subnetMask, row.gateway, row.interfaceIPAddress]) ?? [];
|
||||
return { doc, id, rows };
|
||||
}
|
||||
|
||||
it('take a cell as Java does where Java takes it, and keep it where Java stores null', () => {
|
||||
for (const [name, column, text] of [
|
||||
['leadingZeros', 'destinationNetwork', '010.0.0.0'],
|
||||
['maskCell', 'subnetMask', '255.0.255.0'],
|
||||
] as const) {
|
||||
const { doc, id, rows } = edited();
|
||||
expect('value' in setRouteCell(doc, id, 0, column, text, 'me'), name).toBe(true);
|
||||
expect(rows(), name).toEqual(java.edits[name]!.manual);
|
||||
}
|
||||
for (const [name, text] of [
|
||||
['invalidCell', 'abc'],
|
||||
['emptyCell', ''],
|
||||
['dottedTwice', '10..0.0'],
|
||||
] as const) {
|
||||
// Java: the cell becomes null.
|
||||
expect(java.edits[name]!.manual[0]![0], name).toBeNull();
|
||||
const { doc, id, rows } = edited();
|
||||
expect(setRouteCell(doc, id, 0, 'destinationNetwork', text, 'me'), name).toEqual({ invalid: true });
|
||||
expect(rows()[0]![0], name).toBe('172.16.0.0');
|
||||
}
|
||||
});
|
||||
|
||||
it('edit and lose the row they are asked for', () => {
|
||||
const { doc, id, rows } = edited();
|
||||
addRoute(doc, id, JAVA_MANUAL[0]!, 'me');
|
||||
expect(setRouteCell(doc, id, 1, 'gateway', '192.168.1.3', 'me')).toEqual({ value: '192.168.1.3' });
|
||||
expect(rows()).toEqual([
|
||||
['172.16.0.0', '255.255.0.0', '192.168.0.2', '192.168.0.10'],
|
||||
['172.16.0.0', '255.255.0.0', '192.168.1.3', '192.168.1.1'],
|
||||
]);
|
||||
removeRoute(doc, id, 1, 'me');
|
||||
expect(rows()).toEqual([['172.16.0.0', '255.255.0.0', '192.168.0.2', '192.168.0.10']]);
|
||||
});
|
||||
|
||||
it('take a new row once all four cells pass, as Java does, and lose a row', () => {
|
||||
const cells = { destinationNetwork: '10.9.0.0', subnetMask: '255.255.0.0', gateway: '10.0.0.9', interfaceIPAddress: '' };
|
||||
let { doc, id, rows } = edited();
|
||||
expect(addRoute(doc, id, cells, 'me')).toEqual({ invalid: ['interfaceIPAddress'] });
|
||||
expect(rows()).toEqual(java.edits.newRowThreeCells!.manual);
|
||||
expect(addRoute(doc, id, { ...cells, interfaceIPAddress: '10..0.0.1' }, 'me')).toEqual({ invalid: ['interfaceIPAddress'] });
|
||||
expect(rows()).toEqual(java.edits.newRowInvalidCell!.manual);
|
||||
expect(addRoute(doc, id, { ...cells, interfaceIPAddress: '10.0.0.1' }, 'me')).toEqual({ route: { ...cells, interfaceIPAddress: '10.0.0.1' } });
|
||||
expect(rows()).toEqual(java.edits.newRowFourCells!.manual);
|
||||
expect(addRoute(doc, 'nobody', { ...cells, interfaceIPAddress: '10.0.0.1' }, 'me')).toEqual({ invalid: [] });
|
||||
({ doc, id, rows } = edited());
|
||||
removeRoute(doc, id, 5, 'me');
|
||||
removeRoute(doc, id, 0, 'me');
|
||||
expect(rows()).toEqual(java.edits.removeRow!.manual);
|
||||
expect((doc.getMap<Y.Map<unknown>>(ROOTS.nodes).get(id)!.get('settings') as Y.Map<unknown>).has('routes')).toBe(false);
|
||||
// Java keeps one new row at a time; the sheet's draft is one row too.
|
||||
expect(java.edits.newRowTwice!.rowsShown).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("a home router's sheet", () => {
|
||||
it("shows its WAN and LAN cards, keeps the WAN card in the device's settings, and leaves its DHCP server running", () => {
|
||||
expect(java.flows).toMatchObject({ homeRouterDhcpClient: true, homeRouterDhcpClientStopsServer: false });
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'gateway', 'Heim');
|
||||
const sheet = home(doc, id);
|
||||
expect([sheet.wan.ipAddress, sheet.wan.subnetMask, sheet.lan.ipAddress, sheet.lan.subnetMask, sheet.gateway, sheet.ipForwarding, sheet.dhcpClient]).toEqual([
|
||||
'42.0.0.10',
|
||||
'255.0.0.0',
|
||||
'192.168.0.10',
|
||||
'255.255.255.0',
|
||||
'',
|
||||
true,
|
||||
false,
|
||||
]);
|
||||
expect(setInterfaceAddress(doc, id, sheet.wan.portId, 'ipAddress', '042.1.2.3', 'me')).toEqual({ value: '42.1.2.3' });
|
||||
expect(setInterfaceAddress(doc, id, sheet.lan.portId, 'subnetMask', '255.255.255.128', 'me')).toEqual({ value: '255.255.255.128' });
|
||||
expect(setRouterGateway(doc, id, '42.0.0.1', 'me')).toEqual({ value: '42.0.0.1' });
|
||||
expect(readProject(doc).deviceConfigurations.get(id)).toEqual({ ipAddress: '42.1.2.3', subnetMask: '255.0.0.0', defaultGateway: '42.0.0.1', dnsServer: '' });
|
||||
// Unlike a router, a home router keeps its own settings without a gateway.
|
||||
setRouterGateway(doc, id, '', 'me');
|
||||
expect(readProject(doc).deviceConfigurations.get(id)).toMatchObject({ defaultGateway: '' });
|
||||
const settings = doc.getMap<Y.Map<unknown>>(ROOTS.nodes).get(id)!.get('settings') as Y.Map<unknown>;
|
||||
const server = new Y.Map<unknown>();
|
||||
server.set('isActive', true);
|
||||
settings.set('dhcpServer', server);
|
||||
setDhcpClient(doc, id, true, 'me');
|
||||
expect(home(doc, id).dhcpClient).toBe(true);
|
||||
expect(server.get('isActive')).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps everything through a saved file, with the router’s rows and RIP', () => {
|
||||
const doc = new Y.Doc();
|
||||
const id = add(doc, 'router', 'R', 3);
|
||||
const homeId = add(doc, 'gateway', 'Heim');
|
||||
const cards = router(doc, id).interfaces;
|
||||
setInterfaceAddress(doc, id, cards[2]!.portId, 'ipAddress', '172.16.5.1', 'me');
|
||||
setRouterGateway(doc, id, '192.168.0.254', 'me');
|
||||
setRip(doc, id, true, 'me');
|
||||
addRoute(doc, id, JAVA_MANUAL[1]!, 'me');
|
||||
setInterfaceAddress(doc, homeId, home(doc, homeId).lan.portId, 'ipAddress', '192.168.7.1', 'me');
|
||||
setRouterGateway(doc, homeId, '42.0.0.1', 'me');
|
||||
let counter = 0;
|
||||
const reopened = openProjectArchive(saveProjectArchive(readProject(doc)).archive, () => `40000000-0000-0000-0000-${(++counter).toString(16).padStart(12, '0')}`);
|
||||
const [routerAgain, homeAgain] = reopened.graph.nodes;
|
||||
const address = (node: typeof routerAgain, index: number) => reopened.interfaceConfigurations.get(`${node!.id}/${node!.ports[index]!.id}`)?.ipAddress;
|
||||
expect(address(routerAgain, 2)).toBe('172.16.5.1');
|
||||
expect(reopened.deviceConfigurations.get(routerAgain!.id)?.defaultGateway).toBe('192.168.0.254');
|
||||
expect(reopened.ripEnabledByNodeId.get(routerAgain!.id)).toBe(true);
|
||||
expect(reopened.manualRoutesByNodeId.get(routerAgain!.id)).toEqual([{ destinationNetwork: '10.20.0.0', subnetMask: '255.255.0.0', gateway: '10.0.0.2', interfaceIPAddress: '10.0.0.1' }]);
|
||||
expect(address(homeAgain, 1)).toBe('192.168.7.1');
|
||||
expect(reopened.deviceConfigurations.get(homeAgain!.id)?.defaultGateway).toBe('42.0.0.1');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user