Merge pull request 'licences_switches answers switches carried from the old database with their licence and consumer (issue 1409)' (#664) from fix/1409-carried-switches into main

This commit was merged in pull request #664.
This commit is contained in:
2026-11-12 15:27:13 +00:00
2 changed files with 36 additions and 0 deletions
@@ -92,6 +92,21 @@ func (r *Reader) Tools() []stdio.Tool {
}
}
// carriedForm gives an entry carried over from the old table `audit` (§12) the fields of §37: the old manager wrote a
// switch as `consumer` (one), `licence` (the one moved to), `from` and `by`, and no `consumers` or `to`.
func carriedForm(e map[string]any) {
if _, ok := e["to"]; !ok {
if l, ok := e["licence"]; ok {
e["to"] = l
}
}
if _, ok := e["consumers"]; !ok {
if c, ok := e["consumer"]; ok {
e["consumers"] = []any{c}
}
}
}
// Switches are the audit's switches, newest first: of a licence (from or to it) and a consumer when named, none before
// since, at most limit (§37).
func (r *Reader) Switches(licence, consumer string, since time.Time, limit int) ([]map[string]any, error) {
@@ -118,6 +133,7 @@ func (r *Reader) Switches(licence, consumer string, since time.Time, limit int)
if json.Unmarshal(all[i].Value, &e) != nil {
continue
}
carriedForm(e)
at, _ := time.Parse(time.RFC3339Nano, fmt.Sprint(e["at"]))
if at.Before(since) {
continue
@@ -229,6 +229,26 @@ func TestSwitchesAnswersBothCauses(t *testing.T) {
}
}
// design 39 bus-and-switch §37, §12: a switch carried over from the old table keeps the old field names (consumer,
// licence) and is answered with `to` and `consumers`, and found by them.
func TestSwitchesAnswersCarriedEntries(t *testing.T) {
rt := newRuntime(t)
rt.seedLog(licences.EntryAudit, "switched", map[string]any{"at": "2026-11-11T21:10:33Z", "consumer": "n1",
"licence": "d@example.org", "from": "c@example.org", "by": "a@example.org"})
r := rt.reader()
for _, args := range []map[string]any{nil, {"consumer": "n1"}, {"licence": "d@example.org"}} {
got := run(t, r, "licences_switches", args).([]any)
if len(got) != 1 {
t.Fatalf("%v: %v", args, got)
}
e := got[0].(map[string]any)
if e["to"] != "d@example.org" || e["from"] != "c@example.org" || e["by"] != "a@example.org" ||
len(e["consumers"].([]any)) != 1 || e["consumers"].([]any)[0] != "n1" {
t.Fatalf("%v: %v", args, e)
}
}
}
// design 39 bus-and-switch §40/AC1, B18, design 39 parts §3: a value set at node level is answered with its level, the
// rest with the default, each with its value and default, and an invalid one saying why, read from the settings file;
// mesh-config not answering is an error, never the default.