feat(workflow): publish verified forecast controls and consolidated docs
Tests / test (push) Successful in 1m1s
Tests / test (push) Successful in 1m1s
Approved by Daniel Haefliger for develop and beta. Author dh_Agent, authenticated account dh. Preserve published battery, charging and overall Energy Pie changes. No deployment or plant control authorization.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { workflowView } from './netplan-v4.js';
|
||||
|
||||
const now = Date.parse('2026-10-08T12:00:00Z');
|
||||
const report = (state, overrides = {}) => ({
|
||||
state, learningEnabled: state !== 'disabled', controlRequested: state === 'active',
|
||||
controlActive: state === 'active', planningReady: ['ready', 'active'].includes(state),
|
||||
reportFresh: true, checkedAt: new Date(now).toISOString(),
|
||||
sourceReportAt: new Date(now - 10000).toISOString(), sourceFreshUntil: new Date(now + 110000).toISOString(),
|
||||
reason: '', ...overrides
|
||||
});
|
||||
|
||||
test('workflow reports all six states without deriving control from a plan', () => {
|
||||
for (const [state, label] of Object.entries({ disabled: 'Ausgeschaltet', collecting: 'Sammelt Daten', learning: 'Lernt', ready: 'Bereit', active: 'Aktiv', interrupted: 'Unterbrochen' })) {
|
||||
assert.equal(workflowView(report(state), now).label, label);
|
||||
}
|
||||
assert.equal(workflowView(undefined, now).label, 'Betriebsstatus nicht bestaetigt');
|
||||
assert.equal(workflowView({ plan: { status: 'optimal', runMode: 'shadow' }, acknowledgement: { status: 'shadow_seen' } }, now).state, 'unknown');
|
||||
});
|
||||
|
||||
test('missing, malformed or contradictory reports never claim active control', () => {
|
||||
const malformed = [null, {}, report('other'), report('active', { checkedAt: 'invalid' }),
|
||||
report('active', { checkedAt: 123 }), report('active', { controlActive: false }),
|
||||
report('active', { controlRequested: false }), report('active', { planningReady: false }),
|
||||
report('ready', { controlActive: true }), report('ready', { planningReady: false }),
|
||||
report('active', { learningEnabled: 'true' })];
|
||||
for (const input of malformed) assert.equal(workflowView(input, now).state, 'unknown');
|
||||
});
|
||||
|
||||
test('active requires a fresh manager report and expires at its exact boundary', () => {
|
||||
for (const overrides of [{ reportFresh: false }, { reportFresh: undefined }, { learningEnabled: false },
|
||||
{ sourceReportAt: null }, { sourceReportAt: new Date(now + 1).toISOString() },
|
||||
{ sourceFreshUntil: null }, { sourceFreshUntil: new Date(now).toISOString() },
|
||||
{ sourceFreshUntil: new Date(now - 1).toISOString() }]) {
|
||||
const view = workflowView(report('active', overrides), now);
|
||||
assert.equal(view.state, 'interrupted'); assert.equal(view.controlActive, false);
|
||||
}
|
||||
assert.equal(workflowView(report('active'), now + 109999).state, 'active');
|
||||
assert.equal(workflowView(report('active'), now + 110000).state, 'interrupted');
|
||||
});
|
||||
|
||||
test('known reasons are understandable and unknown reasons remain visible as text', () => {
|
||||
assert.match(workflowView(report('collecting', { reason: 'collecting_measurements' }), now).reason, /Messdaten werden gesammelt/);
|
||||
assert.match(workflowView(report('active', { reason: 'manager_control_active' }), now).reason, /Manager bestaetigt/);
|
||||
const reason = '<script>unexpected_reason</script>';
|
||||
assert.equal(workflowView(report('interrupted', { reason }), now).reason, reason);
|
||||
});
|
||||
|
||||
test('portal release assets match the canonical planner sources', async () => {
|
||||
for (const file of ['netplan-v4.js', 'netplan-v4.css', 'netplan-v4.html']) {
|
||||
const canonical = await readFile(new URL(file, import.meta.url), 'utf8');
|
||||
const release = await readFile(new URL('../../license/changes/20261006-forecast-integration/public/' + file, import.meta.url), 'utf8');
|
||||
assert.equal(release, canonical, file);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user