@@ -0,0 +1,348 @@
import assert from 'node:assert/strict' ;
import fs from 'node:fs' ;
import path from 'node:path' ;
import test from 'node:test' ;
import { assertLiveTrafficSnapshot } from '../../.test-dist/src/shared/liveTraffic.js' ;
import {
groupTrafficConnections ,
reconcileTrafficGroups ,
trafficGroupMatches ,
} from '../../.test-dist/src/web/features/traffic/trafficRows.js' ;
const root = path . resolve ( import . meta . dirname , '../..' ) ;
const source = ( file ) => fs . readFileSync ( path . join ( root , file ) , 'utf8' ) ;
const app = source ( 'src/web/App.tsx' ) ;
const api = source ( 'src/web/api/harborClient.ts' ) ;
const page = source ( 'src/web/components/ClientOverviewPage.tsx' ) ;
const feature = source ( 'src/web/features/traffic/TrafficFeature.tsx' ) ;
const rowModel = source ( 'src/web/features/traffic/trafficRows.ts' ) ;
const boundary = source ( 'src/web/features/traffic/index.ts' ) ;
const styles = source ( 'src/web/styles/features/traffic.css' ) ;
const primitives = source ( 'src/web/styles/primitives.css' ) ;
const validSnapshot = {
apiVersion : 1 ,
epoch : 'epoch-1' ,
sequence : 1 ,
observedAt : '2026-08-31T10:00:00.000Z' ,
capabilities : { lifecycle : true , deviceAttribution : false , applicationAttribution : false } ,
source : {
transport : 'native' ,
state : 'live' ,
completeness : 'lifecycle' ,
singBoxVersion : '1.14.0-rc.5' ,
singBoxApiVersion : 1 ,
error : null ,
unattributedUploadBytes : '0' ,
unattributedDownloadBytes : '0' ,
} ,
summary : {
active : 1 ,
recent : 0 ,
visible : 1 ,
recognized : 1 ,
unresolved : 0 ,
unresolvedOrigin : 0 ,
truncated : false ,
} ,
connections : [ {
id : 'connection-1' ,
startedAt : '2026-08-31T09:59:59.000Z' ,
closedAt : null ,
inbound : { tag : 'mixed-in' , type : 'mixed' } ,
network : 'tcp' ,
protocol : 'tls' ,
source : { ip : '127.0.0.1' , port : 53000 } ,
destination : { domain : 'example.com' , ip : '203.0.113.1' , port : 443 , provenance : 'sing-box' } ,
origin : { kind : 'this-mac' , id : null , label : 'Этот Mac' , provenance : 'client-runtime' } ,
route : {
kind : 'vpn' ,
scope : 'local-sing-box' ,
outbound : 'proxy' ,
outboundType : 'selector' ,
chain : [ 'proxy' ] ,
rule : 'default' ,
} ,
traffic : {
uploadBytes : '10' ,
downloadBytes : '20' ,
uploadBytesPerSecond : '1' ,
downloadBytesPerSecond : '2' ,
} ,
} ] ,
} ;
function trafficConnection ( id , overrides = { } ) {
const base = validSnapshot . connections [ 0 ] ;
return {
... base ,
... overrides ,
id ,
inbound : { ... base . inbound , ... overrides . inbound } ,
source : { ... base . source , ... overrides . source } ,
destination : { ... base . destination , ... overrides . destination } ,
origin : { ... base . origin , ... overrides . origin } ,
route : { ... base . route , ... overrides . route } ,
traffic : { ... base . traffic , ... overrides . traffic } ,
} ;
}
test ( 'live traffic runtime boundary accepts the DTO and rejects inconsistent or malformed snapshots' , ( ) => {
assert . equal ( assertLiveTrafficSnapshot ( validSnapshot ) , validSnapshot ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
summary : { ... validSnapshot . summary , visible : 0 } ,
} ) , /Inconsistent traffic summary/ ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
connections : [ {
... validSnapshot . connections [ 0 ] ,
route : { ... validSnapshot . connections [ 0 ] . route , outbound : 42 } ,
} ] ,
} ) , /Invalid traffic connection/ ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
summary : { ... validSnapshot . summary , active : 0 , recent : 1 , recognized : 0 } ,
connections : [ {
... validSnapshot . connections [ 0 ] ,
closedAt : '2026-08-31T10:00:00.000Z' ,
} ] ,
} ) , /Invalid closed traffic rate/ ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
summary : { ... validSnapshot . summary , active : 2 , visible : 2 , recognized : 2 } ,
connections : [ validSnapshot . connections [ 0 ] , validSnapshot . connections [ 0 ] ] ,
} ) , /Invalid traffic connection identity/ ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
connections : [ { ... validSnapshot . connections [ 0 ] , closedAt : '2026-08-31 10:00:00Z' } ] ,
} ) , /Invalid traffic connection identity/ ) ;
assert . throws ( ( ) => assertLiveTrafficSnapshot ( {
... validSnapshot ,
connections : [ {
... validSnapshot . connections [ 0 ] ,
traffic : { ... validSnapshot . connections [ 0 ] . traffic , uploadBytes : 10 } ,
} ] ,
} ) , /Invalid traffic byte value/ ) ;
} ) ;
test ( 'Mac and Gateway traffic drawers use one feature boundary and the cached read-only endpoint' , ( ) => {
assert . match ( boundary , /TrafficPanel,[\s\S]*TrafficToggle,[\s\S]*useTrafficFeature/ ) ;
assert . match ( api , /traffic: \{[\s\S]*live: \(\) => request\('\/api\/traffic\/live'\)/ ) ;
assert . match ( app , /loadLiveTraffic: api\.traffic\.live/ ) ;
assert . match ( page , /useTrafficFeature\(\{[\s\S]*enabled: true,[\s\S]*isGateway,[\s\S]*loadLiveTraffic: actions\.loadLiveTraffic/ ) ;
assert . match ( feature , /interface TrafficFeatureOptions \{[\s\S]*isGateway: boolean/ ) ;
assert . match ( feature , /return \{[\s\S]*isGateway,[\s\S]*isOpen/ ) ;
const rail = page . slice ( page . indexOf ( '<nav' ) , page . indexOf ( '</nav>' ) ) ;
const devices = rail . indexOf ( '<DevicesToggle' ) ;
const traffic = rail . indexOf ( '<TrafficToggle' ) ;
const diagnostics = rail . indexOf ( '<DiagnosticsToggle' ) ;
assert . ok ( devices >= 0 && traffic > devices && diagnostics > traffic ) ;
assert . equal ( ( rail . match ( /<TrafficToggle/g ) || [ ] ) . length , 1 ) ;
assert . doesNotMatch ( rail , /!isGateway\s*&&\s*<TrafficToggle/ ) ;
assert . match ( page , /\{hasSubscription && <TrafficPanel feature=\{trafficFeature\} \/>}/ ) ;
assert . doesNotMatch ( page , /!isGateway && hasSubscription && <TrafficPanel/ ) ;
assert . doesNotMatch ( page , /if \(isGateway\) trafficFeature\.close\(\)/ ) ;
assert . match ( page , /DRAWER_ORDER = \['subscription', 'failover', 'instructions', 'devices', 'traffic', 'diagnostics', 'routing', 'journal'\]/ ) ;
assert . doesNotMatch ( feature , /from ['"][^'"]*\/api(?:\/|\.js)/ ) ;
} ) ;
test ( 'traffic polling runs every second only while the drawer is open and unpaused' , ( ) => {
assert . match ( feature , /const POLL_MS = 1_000/ ) ;
assert . match ( feature , /if \(!enabled \|\| !isOpen \|\| paused\) return undefined/ ) ;
assert . match ( feature , /assertLiveTrafficSnapshot\(await loadLiveTraffic\(\)\)/ ) ;
assert . match ( feature , /setSnapshot\(next\)[\s\S]*setRequestState\('ready'\)/ ) ;
assert . match ( feature , /catch \{[\s\S]*setRequestState\('error'\)/ ) ;
assert . match ( feature , /timer = setTimeout\(poll, POLL_MS\)/ ) ;
assert . match ( feature , /cancelled = true[\s\S]*clearTimeout\(timer\)/ ) ;
assert . match ( feature , /aria-pressed=\{feature\.paused\}[\s\S]*Продолжить[\s\S]*Пауза/ ) ;
assert . doesNotMatch ( feature , /setInterval|WebSocket|EventSource/ ) ;
} ) ;
test ( 'traffic drawer exposes the requested truthful states and accessible controls' , ( ) => {
for ( const copy of [
'VPN остановлен' ,
'Активных соединений пока нет' ,
'Инспектор трафика временно недоступен' ,
'Эта версия sing-box не поддерживает инспектор трафика' ,
'Инспектор трафика выключен в настройках Harbor Connect.' ,
'Инспектор трафика выключен в настройках Harbor Gateway.' ,
'Показан последний полученный снимок' ,
'Только трафик через Harbor Connect. Приложения macOS недоступны внутри Docker.' ,
'Только соединения, прошедшие через sing-box Gateway. Трафик, обходящий sing-box напрямую, здесь не виден.' ,
] ) assert . match ( feature , new RegExp ( copy . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ) ) ;
assert . match ( feature , /\{feature\.isGateway \? 'GATEWAY' : 'MAC'\} · \{snapshot\?\.summary\.active \|\| 0\} АКТИВНЫХ/ ) ;
assert . match ( feature , /feature\.isGateway[\s\S]*Инспектор трафика выключен в настройках Harbor Gateway\.[\s\S]*Инспектор трафика выключен в настройках Harbor Connect\./ ) ;
assert . match ( feature , /feature\.isGateway[\s\S]*Только соединения, прошедшие через sing-box Gateway\. Трафик, обходящий sing-box напрямую, здесь не виден\.[\s\S]*Только трафик через Harbor Connect\. Приложения macOS недоступны внутри Docker\./ ) ;
assert . match ( feature , /type="search"[\s\S]*aria-label="Найти домен, сервис или IP"/ ) ;
assert . match ( feature , /role="group" aria-label="Фильтр по маршруту"/ ) ;
assert . match ( feature , /role="group" aria-label="Фильтр по качеству распознавания"/ ) ;
assert . match ( feature , /aria-pressed=\{routeFilter === value\}/ ) ;
assert . match ( feature , /aria-pressed=\{qualityFilter === value\}/ ) ;
assert . match ( feature , /const \[expandedId, setExpandedId\] = useState\(''\)/ ) ;
assert . match ( feature , /aria-expanded=\{expanded\}[\s\S]*aria-controls=\{detailsId\}/ ) ;
assert . match ( feature , /Источник[\s\S]*Назначение[\s\S]*Правило[\s\S]*Цепочка/ ) ;
assert . doesNotMatch ( feature , /closeConnection|reroute|history|sessionStorage/ ) ;
} ) ;
test ( 'traffic retention is local, bounded to approved choices and uses the frozen server observation clock' , ( ) => {
assert . match ( feature , /const RETENTION_STORAGE_KEY = 'harbor:traffic-retention-seconds'/ ) ;
assert . match ( feature , /const RETENTION_OPTIONS = \[5, 10, 30\] as const/ ) ;
assert . match ( feature , /Number\(localStorage\.getItem\(RETENTION_STORAGE_KEY\)\)/ ) ;
assert . match ( feature , /RETENTION_OPTIONS\.includes\(value as RetentionSeconds\)[\s\S]*: 10/ ) ;
assert . match ( feature , /localStorage\.setItem\(RETENTION_STORAGE_KEY, String\(seconds\)\)/ ) ;
assert . match ( feature , /Показывать завершённые/ ) ;
assert . match ( feature , /aria-label="Время показа завершённых соединений"/ ) ;
assert . match ( feature , /snapshot\?\.observedAt \? Date\.parse\(snapshot\.observedAt\) : Number\.NaN/ ) ;
assert . match ( feature , /connection\.closedAt === null \|\| !Number\.isFinite\(snapshotTime\)/ ) ;
assert . match ( feature , /snapshotTime - Date\.parse\(connection\.closedAt\) < retentionSeconds \* 1_000/ ) ;
assert . match ( feature , /groupTrafficConnections\(retainedConnections\)/ ) ;
assert . match ( feature , /trafficGroups\.filter\([\s\S]*trafficGroupMatches/ ) ;
assert . match ( feature , /group\.connections\.length === 1[\s\S]*`Завершено · \$\{group\.protocol\}`/ ) ;
assert . match ( feature , /Соединения сгруппированы по назначению, протоколу и маршруту\./ ) ;
} ) ;
test ( 'traffic groups combine compatible UUIDs with exact byte sums and whole-group search' , ( ) => {
const groups = groupTrafficConnections ( [
trafficConnection ( 'active' , {
destination : { domain : 'yandex.ru' , ip : '203.0.113.1' } ,
traffic : {
uploadBytes : '10' ,
downloadBytes : '20' ,
uploadBytesPerSecond : '1' ,
downloadBytesPerSecond : '2' ,
} ,
} ) ,
trafficConnection ( 'closed-a' , {
closedAt : '2026-08-31T09:59:59.500Z' ,
destination : { domain : 'yandex.ru' , ip : '203.0.113.2' } ,
traffic : {
uploadBytes : '30' ,
downloadBytes : '40' ,
uploadBytesPerSecond : '0' ,
downloadBytesPerSecond : '0' ,
} ,
} ) ,
trafficConnection ( 'closed-b' , {
closedAt : '2026-08-31T09:59:59.700Z' ,
destination : { domain : 'yandex.ru' , ip : '203.0.113.2' } ,
traffic : {
uploadBytes : '9007199254740993' ,
downloadBytes : '9007199254740995' ,
uploadBytesPerSecond : '0' ,
downloadBytesPerSecond : '0' ,
} ,
} ) ,
] ) ;
assert . equal ( groups . length , 1 ) ;
assert . equal ( groups [ 0 ] . label , 'yandex.ru' ) ;
assert . equal ( groups [ 0 ] . activeCount , 1 ) ;
assert . equal ( groups [ 0 ] . recentCount , 2 ) ;
assert . deepEqual ( groups [ 0 ] . connections . map ( ( { id } ) => id ) , [ 'active' , 'closed-a' , 'closed-b' ] ) ;
assert . deepEqual ( groups [ 0 ] . destinationIps , [ '203.0.113.1' , '203.0.113.2' ] ) ;
assert . deepEqual ( groups [ 0 ] . traffic , {
uploadBytes : '9007199254741033' ,
downloadBytes : '9007199254741055' ,
uploadBytesPerSecond : '1' ,
downloadBytesPerSecond : '2' ,
} ) ;
assert . equal ( trafficGroupMatches ( groups [ 0 ] , '203.0.113.2' , 'all' , 'all' ) , true ) ;
assert . equal ( trafficGroupMatches ( groups [ 0 ] , 'missing.test' , 'all' , 'all' ) , false ) ;
assert . equal ( trafficGroupMatches ( groups [ 0 ] , '' , 'vpn' , 'recognized' ) , true ) ;
assert . equal ( trafficGroupMatches ( groups [ 0 ] , '' , 'direct' , 'all' ) , false ) ;
} ) ;
test ( 'traffic grouping keeps incompatible and unknown destinations separate' , ( ) => {
const base = trafficConnection ( 'base' , { destination : { domain : 'example.com' , ip : '203.0.113.1' } } ) ;
const same = trafficConnection ( 'same' , { destination : { domain : 'Example.COM' , ip : '203.0.113.2' } } ) ;
assert . equal ( groupTrafficConnections ( [ base , same ] ) . length , 1 ) ;
const variants = [
[ 'domain' , { destination : { domain : 'www.example.com' , ip : '203.0.113.1' } } ] ,
[ 'port' , { destination : { domain : 'example.com' , ip : '203.0.113.1' , port : 80 } } ] ,
[ 'network' , { network : 'udp' } ] ,
[ 'protocol' , { protocol : 'http' } ] ,
[ 'origin' , { origin : { kind : 'device' , id : 'device-1' , label : 'iPhone' , provenance : 'source-ip' } } ] ,
[ 'route kind' , { route : { kind : 'other' } } ] ,
[ 'route outbound' , { route : { outbound : 'other-proxy' } } ] ,
[ 'route outbound type' , { route : { outboundType : 'vless' } } ] ,
[ 'route chain' , { route : { chain : [ 'proxy' , 'hop' ] } } ] ,
[ 'route rule' , { route : { rule : 'other-rule' } } ] ,
] ;
for ( const [ name , overrides ] of variants ) {
assert . equal ( groupTrafficConnections ( [ base , trafficConnection ( String ( name ) , overrides ) ] ) . length , 2 , name ) ;
}
assert . equal ( groupTrafficConnections ( [
trafficConnection ( 'ip-a' , { destination : { domain : null , ip : '203.0.113.1' } } ) ,
trafficConnection ( 'ip-b' , { destination : { domain : null , ip : '203.0.113.2' } } ) ,
] ) . length , 2 ) ;
assert . equal ( groupTrafficConnections ( [
trafficConnection ( 'unknown-a' , { destination : { domain : null , ip : null , provenance : 'unknown' } } ) ,
trafficConnection ( 'unknown-b' , { destination : { domain : null , ip : null , provenance : 'unknown' } } ) ,
] ) . length , 2 ) ;
const deviceOrigin = { kind : 'device' , id : 'device-1' , label : 'iPhone' , provenance : 'source-ip' } ;
const [ deviceGroup ] = groupTrafficConnections ( [
trafficConnection ( 'device' , { origin : deviceOrigin } ) ,
] ) ;
assert . deepEqual ( deviceGroup . origin , deviceOrigin ) ;
assert . match ( feature , /const source = onlyConnection[\s\S]*group\.origin\.label[\s\S]*onlyConnection\.source\.ip[\s\S]*group\.connections\.length/ ) ;
} ) ;
test ( 'traffic groups stay mounted and inert through exit while the same group cancels removal' , ( ) => {
const group = ( id , count = 1 ) => ( { id , connections : Array . from ( { length : count } ) } ) ;
let rows = reconcileTrafficGroups ( [ ] , [ group ( 'a' ) , group ( 'b' ) , group ( 'c' ) ] , false ) ;
rows = reconcileTrafficGroups ( rows , [ group ( 'b' , 2 ) ] , false ) ;
assert . deepEqual ( rows . map ( ( row ) => [ row . group . id , row . group . connections . length , row . exiting ] ) , [
[ 'a' , 1 , true ] ,
[ 'b' , 2 , false ] ,
[ 'c' , 1 , true ] ,
] ) ;
rows = reconcileTrafficGroups ( rows , [ group ( 'a' ) , group ( 'b' , 3 ) , group ( 'c' ) ] , false ) ;
assert . deepEqual ( rows . map ( ( row ) => [ row . group . id , row . group . connections . length , row . exiting ] ) , [
[ 'a' , 1 , false ] ,
[ 'b' , 3 , false ] ,
[ 'c' , 1 , false ] ,
] ) ;
assert . deepEqual ( reconcileTrafficGroups ( rows , [ ] , true ) , [ ] ) ;
assert . match ( rowModel , /const desiredIds = new Set\(desired\.map\(\(group\) => group\.id\)\)/ ) ;
assert . match ( feature , /groupTrafficConnections\(retainedConnections\)/ ) ;
assert . match ( feature , /trafficGroupMatches\(group, query, routeFilter, qualityFilter\)/ ) ;
assert . match ( feature , /reconcileTrafficGroups\(current, groups, immediate\)/ ) ;
assert . match ( feature , /setExpandedId\(\(current\) => desiredIds\.has\(current\) \? current : ''\)/ ) ;
assert . match ( feature , /inert=\{exiting \|\| undefined\}/ ) ;
assert . match ( feature , /event\.target === event\.currentTarget[\s\S]*event\.animationName === 'client-traffic-connection-out'/ ) ;
assert . match ( feature , /row\.group\.id !== id \|\| !row\.exiting/ ) ;
assert . match ( feature , /matchMedia\('\(prefers-reduced-motion: reduce\)'\)[\s\S]*media\.addEventListener\('change', update\)[\s\S]*media\.removeEventListener\('change', update\)/ ) ;
assert . match ( feature , /const immediate = reducedMotion[\s\S]*\['disabled', 'incompatible', 'stopped'\]\.includes/ ) ;
assert . match ( feature , /aria-label="Группы активных и недавно завершённых соединений"/ ) ;
assert . match ( feature , /group\.connections\.length > 1[\s\S]*× \$\{group\.connections\.length\}/ ) ;
assert . match ( feature , /group\.activeCount > 0 && <strong>/ ) ;
assert . match ( feature , /displayedGroups\.map\(\(row\) => <TrafficGroupRow[\s\S]*key=\{row\.group\.id\}/ ) ;
assert . match ( feature , /<b>Активных распознано<\/b>/ ) ;
assert . match ( feature , /<b>Активных требует внимания<\/b>/ ) ;
} ) ;
test ( 'traffic styling preserves the shared drawer geometry and minimal motion' , ( ) => {
assert . match ( styles , /\.client-traffic-toggle svg \{[\s\S]*width: 24px;[\s\S]*height: 24px/ ) ;
assert . match ( primitives , /\.client-drawer \{[\s\S]*width: min\(580px, 100vw\)/ ) ;
assert . match ( styles , /@media \(max-width: 768px\) \{[\s\S]*\.client-traffic \{[\s\S]*width: 100vw/ ) ;
assert . doesNotMatch ( styles , /overflow-y:\s*(?:auto|scroll)/ ) ;
assert . match ( styles , /\.client-traffic-meta button \{[\s\S]*width: 96px/ ) ;
assert . match ( styles , /\.client-traffic-details \{[\s\S]*animation: client-traffic-details-in 180ms/ ) ;
assert . match ( styles , /\.client-traffic-connection \{[\s\S]*animation: client-traffic-connection-in 420ms/ ) ;
assert . match ( styles , /\.client-traffic-connection\.is-exiting \{[\s\S]*animation: client-traffic-connection-out 240ms/ ) ;
const detailMotion = /@keyframes client-traffic-details-in \{([\s\S]*?)\n\}/ . exec ( styles ) ? . [ 1 ] || '' ;
assert . match ( detailMotion , /opacity:/ ) ;
assert . match ( detailMotion , /translateY/ ) ;
assert . doesNotMatch ( detailMotion , /height|width|margin|padding|scale|filter/ ) ;
for ( const name of [ 'client-traffic-connection-in' , 'client-traffic-connection-out' ] ) {
const rowMotion = new RegExp ( ` @keyframes ${ name } \\ {([ \\ s \\ S]*?) \\ n \\ } ` ) . exec ( styles ) ? . [ 1 ] || '' ;
assert . match ( rowMotion , /opacity:/ ) ;
assert . match ( rowMotion , /translateY/ ) ;
assert . doesNotMatch ( rowMotion , /height|width|margin|padding|scale|filter/ ) ;
}
assert . match ( styles , /@media \(prefers-reduced-motion: reduce\) \{[\s\S]*\.client-traffic-connection,[\s\S]*\.client-traffic-details \{[\s\S]*animation: none/ ) ;
} ) ;