Code review fixes: app 2.9.0, server 2.2.0
Telemetry no longer loses queued batches. The server drops a bad value, or a sample without a plausible time, instead of rejecting the batch, and answers 200 with what it dropped. The app sends only values inside the server's ranges (speed as a magnitude, so reversing is fine) and no samples before the head unit's clock is set. Server: - Pairing hands out and accepts a token only after the device is saved; a failed save leaves the request pending. - A paired token serves only the installation id it was paired with (403 otherwise); the shared INGEST_TOKEN still serves any. - Wrong-password lockouts and open pairing requests are limited per client address (last X-Forwarded-For entry), so a stranger cannot lock the owner out; a car starting again replaces its own request. - ABRP forwarding skips samples stamped ahead of the server's clock, which used to stop it until a restart. App: - Settings written to plaintext during a keystore failure move back into encrypted storage; the stored installation id wins. - The installation id is derived from ANDROID_ID on first start, so a reinstall finds its history; a stored or imported id still wins. - The scanned config file is deleted after import (it holds the token). - GPS stops while live telemetry is off or the server unconfigured, and pauses while parked and idle, keeping the fix the car parked with. - Range estimates use the monotonic clock. - One HTTP helper (ServerHttp) for telemetry, sync and pairing. - Last Turkish strings translated; mise logs shows the NilsMG4 tags. Grafana reads InfluxDB with its own read-only token (new grafana-influx Secret, created by hand as described in k8s/README.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
@@ -311,4 +311,45 @@ public class TelemetryPayloadTest {
|
||||
assertTrue(summary, summary.startsWith("utc=1700000000"));
|
||||
assertTrue(summary, summary.contains("| omitted: soc,speed,"));
|
||||
}
|
||||
|
||||
// ---------- The server's ranges (server/model.go) ----------
|
||||
|
||||
@Test
|
||||
public void reversingSendsSpeedAsAMagnitude() throws Exception {
|
||||
TelemetryPayload t = new TelemetryPayload(1_700_000_000L);
|
||||
t.speedKmh = -4.2f;
|
||||
assertEquals(4, new JSONObject(t.build()).getInt("speed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valuesOutsideTheServersRangeAreLeftOut() throws Exception {
|
||||
TelemetryPayload t = new TelemetryPayload(1_700_000_000L);
|
||||
t.soc = 55;
|
||||
t.extTempC = 72f; // plausible to the car's guard, above the server's 70
|
||||
t.hvacSetpointC = 50f; // a HI sentinel, above the server's 40
|
||||
t.rangeKm = 2500;
|
||||
JSONObject json = new JSONObject(t.build());
|
||||
assertEquals(55, json.getInt("soc"));
|
||||
assertFalse(json.has("ext_temp"));
|
||||
assertFalse(json.has("hvac_setpoint"));
|
||||
assertFalse(json.has("est_battery_range"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theAppsLimitsMatchEveryNumericServerField() {
|
||||
String[] serverFields = {"soc", "speed", "power", "est_battery_range", "capacity", "soe",
|
||||
"kwh_charged", "odometer", "lat", "lon", "elevation", "heading", "batt_temp",
|
||||
"cabin_temp", "ext_temp", "hvac_setpoint", "voltage", "current",
|
||||
"tire_pressure_fl", "tire_pressure_fr", "tire_pressure_rl", "tire_pressure_rr"};
|
||||
for (String field : serverFields) {
|
||||
assertTrue(field, TelemetryPayload.SERVER_LIMITS.containsKey(field));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aClockBefore2001IsNotATime() {
|
||||
assertFalse(TelemetryPayload.isPlausibleUtc(0L));
|
||||
assertFalse(TelemetryPayload.isPlausibleUtc(999_999_999L));
|
||||
assertTrue(TelemetryPayload.isPlausibleUtc(1_790_000_000L));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user