Add device traffic reset with outbound baselines
This commit is contained in:
@@ -517,7 +517,9 @@ test('device outbound history keeps sing-box routes and kernel Direct on separat
|
||||
let trackedDirect = ['10', '20'];
|
||||
let unknown = ['1', '2'];
|
||||
let directIpv4 = ['40', '60'];
|
||||
const service = createDeviceInventoryService({
|
||||
let gateway = ['100', '200'];
|
||||
let proxyIngress = ['10', '20'];
|
||||
const createService = () => createDeviceInventoryService({
|
||||
store,
|
||||
observe: () => ({
|
||||
observedAt,
|
||||
@@ -531,7 +533,8 @@ test('device outbound history keeps sing-box routes and kernel Direct on separat
|
||||
source: { error: null },
|
||||
direct: { uploadBytes: directIpv4[0], downloadBytes: directIpv4[1] },
|
||||
devices: [{
|
||||
ip: '192.168.50.7', mac, interface: 'eth0', uploadBytes: '100', downloadBytes: '200',
|
||||
ip: '192.168.50.7', mac, interface: 'eth0', uploadBytes: gateway[0], downloadBytes: gateway[1],
|
||||
proxyUploadBytes: proxyIngress[0], proxyDownloadBytes: proxyIngress[1],
|
||||
directUploadBytes: directIpv4[0], directDownloadBytes: directIpv4[1],
|
||||
}],
|
||||
}),
|
||||
@@ -547,6 +550,7 @@ test('device outbound history keeps sing-box routes and kernel Direct on separat
|
||||
series: [],
|
||||
}),
|
||||
});
|
||||
let service = createService();
|
||||
|
||||
let snapshot = await service.refresh();
|
||||
assert.deepEqual(snapshot.devices[0].outboundTraffic, {
|
||||
@@ -582,7 +586,64 @@ test('device outbound history keeps sing-box routes and kernel Direct on separat
|
||||
unknownBytes: '9',
|
||||
}]);
|
||||
|
||||
const globalBeforeReset = structuredClone(snapshot.traffic);
|
||||
const resetRevision = snapshot.revision;
|
||||
snapshot = await service.resetTraffic(resetRevision);
|
||||
assert.equal(snapshot.devices[0].uploadBytes, '0');
|
||||
assert.equal(snapshot.devices[0].downloadBytes, '0');
|
||||
assert.equal(snapshot.devices[0].proxyUploadBytes, '0');
|
||||
assert.equal(snapshot.devices[0].proxyDownloadBytes, '0');
|
||||
assert.deepEqual(snapshot.devices[0].trafficHistory, []);
|
||||
assert.deepEqual(snapshot.devices[0].outboundTraffic, {
|
||||
observedAt,
|
||||
singboxObservedAt: observedAt,
|
||||
directIpv4ObservedAt: observedAt,
|
||||
vpnBytes: '0',
|
||||
directTrackedBytes: '0',
|
||||
directIpv4Bytes: '0',
|
||||
unknownBytes: '0',
|
||||
});
|
||||
assert.deepEqual(snapshot.devices[0].outboundTrafficHistory, []);
|
||||
assert.deepEqual(snapshot.traffic, globalBeforeReset);
|
||||
assert.deepEqual(store.read().traffic.outboundBaselinesByDeviceId[id], {
|
||||
routeEpoch: 'epoch-a',
|
||||
directEpoch: 'epoch-a',
|
||||
vpnBytes: '400',
|
||||
directTrackedBytes: '80',
|
||||
directIpv4Bytes: '160',
|
||||
unknownBytes: '12',
|
||||
});
|
||||
assert.equal(service.metricsSnapshot().domainTraffic.routes[0].uploadBytes, '150');
|
||||
assert.equal(service.metricsSnapshot().directTraffic.series[0].uploadBytes, '70');
|
||||
await assert.rejects(
|
||||
service.resetTraffic(resetRevision),
|
||||
(error) => error.code === 'STATE_CONFLICT',
|
||||
);
|
||||
|
||||
observedAt = '2026-08-12T12:00:30.000Z';
|
||||
gateway = ['115', '225'];
|
||||
proxyIngress = ['13', '24'];
|
||||
vpn = ['160', '260'];
|
||||
trackedDirect = ['35', '55'];
|
||||
unknown = ['4', '8'];
|
||||
directIpv4 = ['75', '95'];
|
||||
service = createService();
|
||||
snapshot = await service.refresh();
|
||||
assert.equal(snapshot.devices[0].uploadBytes, '15');
|
||||
assert.equal(snapshot.devices[0].downloadBytes, '25');
|
||||
assert.equal(snapshot.devices[0].proxyUploadBytes, '3');
|
||||
assert.equal(snapshot.devices[0].proxyDownloadBytes, '4');
|
||||
assert.deepEqual(snapshot.devices[0].outboundTraffic, {
|
||||
observedAt,
|
||||
singboxObservedAt: observedAt,
|
||||
directIpv4ObservedAt: observedAt,
|
||||
vpnBytes: '20',
|
||||
directTrackedBytes: '10',
|
||||
directIpv4Bytes: '10',
|
||||
unknownBytes: '0',
|
||||
});
|
||||
|
||||
observedAt = '2026-08-12T12:00:45.000Z';
|
||||
epoch = 'epoch-b';
|
||||
vpn = ['3', '4'];
|
||||
trackedDirect = ['1', '2'];
|
||||
|
||||
@@ -34,9 +34,13 @@ function createHarness({ inventory = {}, body = {} } = {}) {
|
||||
calls.push(['update', ...args]);
|
||||
return inventory.update ?? { revision: 3 };
|
||||
},
|
||||
resetTraffic: async (...args) => {
|
||||
calls.push(['resetTraffic', ...args]);
|
||||
return inventory.resetTraffic ?? { revision: 4 };
|
||||
},
|
||||
setPolicy: async (...args) => {
|
||||
calls.push(['setPolicy', ...args]);
|
||||
return inventory.setPolicy ?? { revision: 4 };
|
||||
return inventory.setPolicy ?? { revision: 5 };
|
||||
},
|
||||
};
|
||||
const route = createDeviceInventoryRoute({
|
||||
@@ -97,13 +101,23 @@ test('device route forwards metadata patch and policy arguments without coercion
|
||||
url: `/api/devices/${deviceId}/policy?source=ui`,
|
||||
}, policyResponse), true);
|
||||
assert.deepEqual(policy.calls, [['setPolicy', deviceId, 42, '8']]);
|
||||
assert.deepEqual(policyResponse.payload, { revision: 4 });
|
||||
assert.deepEqual(policyResponse.payload, { revision: 5 });
|
||||
|
||||
const reset = createHarness({ body: { expectedRevision: '9', ignored: true } });
|
||||
const resetResponse = response();
|
||||
assert.equal(await reset.route.handle({
|
||||
method: 'DELETE',
|
||||
url: '/api/devices/traffic?source=ui',
|
||||
}, resetResponse), true);
|
||||
assert.deepEqual(reset.calls, [['resetTraffic', '9']]);
|
||||
assert.deepEqual(resetResponse.payload, { revision: 4 });
|
||||
});
|
||||
|
||||
test('device route preserves endpoint gating and strict lowercase IDs', async () => {
|
||||
for (const [method, url] of [
|
||||
['POST', '/api/devices'],
|
||||
['GET', '/api/devices/refresh'],
|
||||
['GET', '/api/devices/traffic'],
|
||||
['GET', `/api/devices/${deviceId}`],
|
||||
['POST', `/api/devices/${deviceId}/policy`],
|
||||
]) {
|
||||
@@ -130,6 +144,7 @@ test('device route preserves endpoint gating and strict lowercase IDs', async ()
|
||||
for (const [method, url] of [
|
||||
['GET', '/api/devices'],
|
||||
['POST', '/api/devices/refresh'],
|
||||
['DELETE', '/api/devices/traffic'],
|
||||
['PUT', `/api/devices/${deviceId}`],
|
||||
['PUT', `/api/devices/${deviceId}/policy`],
|
||||
]) {
|
||||
@@ -149,6 +164,7 @@ test('device route propagates synchronous and asynchronous service errors unchan
|
||||
snapshot: () => { throw syncError; },
|
||||
refresh: async () => ({}),
|
||||
update: () => ({}),
|
||||
resetTraffic: async () => ({}),
|
||||
setPolicy: async () => ({}),
|
||||
},
|
||||
readBody: async () => ({}),
|
||||
@@ -164,6 +180,7 @@ test('device route propagates synchronous and asynchronous service errors unchan
|
||||
snapshot: () => ({}),
|
||||
refresh: async () => { throw asyncError; },
|
||||
update: () => ({}),
|
||||
resetTraffic: async () => ({}),
|
||||
setPolicy: async () => ({}),
|
||||
},
|
||||
readBody: async () => ({}),
|
||||
|
||||
Reference in New Issue
Block a user