Track charging sessions and daily consumption history

This commit is contained in:
TCFDONMEZ
2026-10-08 13:39:40 +03:00
parent 9a6c121d09
commit 1a37e52eda
13 changed files with 527 additions and 129 deletions
+36 -31
View File
@@ -3,50 +3,55 @@ import { after, before, test } from "node:test";
import { createApp } from "../src/app.js";
const token = "a-secure-test-token-with-24-chars";
const writes = [];
const collection = {
async updateOne(filter, update, options) { writes.push({ filter, update, options }); }
};
let server;
let baseUrl;
const consumptionWrites = [], chargingWrites = [];
const readable = (writes) => ({
async updateOne(filter, update, options) { writes.push({ filter, update, options }); },
find() { return { sort() { return this; }, limit() { return this; }, async toArray() { return []; } }; }
});
let server, baseUrl;
before(async () => {
const app = createApp({ collection, apiToken: token });
const app = createApp({ consumptionCollection: readable(consumptionWrites),
chargingCollection: readable(chargingWrites), apiToken: token });
await new Promise((resolve) => { server = app.listen(0, "127.0.0.1", resolve); });
baseUrl = `http://127.0.0.1:${server.address().port}`;
});
after(() => new Promise((resolve) => server.close(resolve)));
const validBody = {
installationId: "123e4567-e89b-12d3-a456-426614174000",
recordedAt: 1791417600000,
lifetime: { km: 1000, kwh: 170, hours: 30, socDrop: 400 },
sohPercent: 91.5
};
const auth = { "content-type": "application/json", authorization: `Bearer ${token}` };
const installationId = "123e4567-e89b-42d3-a456-426614174000";
const consumption = { installationId, recordedAt: 1791417600000, date: "2026-10-08",
day: { km: 12, kwh: 2.1, hours: .4, socDrop: 4 },
lifetime: { km: 1000, kwh: 170, hours: 30, socDrop: 400 }, sohPercent: 91.5 };
test("rejects requests without the API token", async () => {
const response = await fetch(`${baseUrl}/v1/consumption`, { method: "PUT", body: JSON.stringify(validBody), headers: { "content-type": "application/json" } });
const response = await fetch(`${baseUrl}/v1/consumption/daily`, { method: "PUT", body: JSON.stringify(consumption), headers: { "content-type": "application/json" } });
assert.equal(response.status, 401);
});
test("validates malformed all-time totals", async () => {
const response = await fetch(`${baseUrl}/v1/consumption`, {
method: "PUT",
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
body: JSON.stringify({ ...validBody, lifetime: { ...validBody.lifetime, km: "12" } })
});
assert.equal(response.status, 400);
test("upserts one daily consumption record", async () => {
const response = await fetch(`${baseUrl}/v1/consumption/daily`, { method: "PUT", headers: auth, body: JSON.stringify(consumption) });
assert.equal(response.status, 204);
assert.deepEqual(consumptionWrites.at(-1).filter, { installationId, date: "2026-10-08" });
assert.equal(consumptionWrites.at(-1).update.$set.day.kwh, 2.1);
});
test("upserts one all-time record per installation", async () => {
const response = await fetch(`${baseUrl}/v1/consumption`, {
method: "PUT",
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
body: JSON.stringify(validBody)
});
test("upserts a complete charging session with its curve and price", async () => {
const sessionId = "223e4567-e89b-42d3-a456-426614174001";
const session = { installationId, sessionId, startedAt: 1791417600000, endedAt: 1791421200000,
durationSeconds: 3600, energyKwh: 50, pricePerKwh: 8.5, totalCost: 425,
startSocPercent: 10, endSocPercent: 90,
curve: [{ timestampMs: 1791417600000, socPercent: 10, powerKw: 72 }] };
const response = await fetch(`${baseUrl}/v1/charging-sessions/${sessionId}`,
{ method: "PUT", headers: auth, body: JSON.stringify(session) });
assert.equal(response.status, 204);
assert.deepEqual(writes.at(-1).filter, { installationId: validBody.installationId });
assert.equal(writes.at(-1).options.upsert, true);
assert.equal(writes.at(-1).update.$set.lifetime.kwh, 170);
assert.equal(writes.at(-1).update.$set.sohPercent, 91.5);
assert.deepEqual(chargingWrites.at(-1).filter, { installationId, sessionId });
assert.equal(chargingWrites.at(-1).update.$set.totalCost, 425);
assert.equal(chargingWrites.at(-1).update.$set.curve[0].powerKw, 72);
});
test("read APIs are authenticated and mobile-client ready", async () => {
const response = await fetch(`${baseUrl}/v1/charging-sessions/${installationId}`, { headers: { authorization: `Bearer ${token}` } });
assert.equal(response.status, 200);
assert.deepEqual(await response.json(), { records: [] });
});