package ankersolix import "testing" // The site views are only worth asking for once the by-serial lookup has found a // site, and that lookup names it differently depending on which shape answered. func TestSiteIDFromReadsWhicheverKeyTheLookupUsed(t *testing.T) { cases := []struct { name string attrs map[string]string want string }{ {"snake case", map[string]string{"site_id": "s-1"}, "s-1"}, {"camel case", map[string]string{"siteId": "s-2"}, "s-2"}, {"nested", map[string]string{"site_info.site_id": "s-3"}, "s-3"}, {"standalone charger", map[string]string{"device_sn": "AT1"}, ""}, {"blank is not an answer", map[string]string{"site_id": " "}, ""}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { views := []chargerDetailView{{ID: "ocpp", Attrs: map[string]string{"site_id": "wrong"}}, {ID: "siteBySn", Attrs: tc.attrs}} if got := siteIDFrom(views); got != tc.want { t.Fatalf("siteIDFrom = %q, want %q", got, tc.want) } }) } } // "source 0" is a number until the account's endpoint list gives it an address. func TestNoteOcppSourceNamesTheBackend(t *testing.T) { views := []chargerDetailView{ {ID: "ocpp", Attrs: map[string]string{"source": "1"}}, {ID: "ocppEndpoints", Attrs: map[string]string{ "list[0].source": "0", "list[0].url": "wss://ocpp.anker.example/0", "list[1].source": "1", "list[1].url": "wss://ocpp.anker.example/1", }}, } noteOcppSource(views) if want := "source 1 = wss://ocpp.anker.example/1"; views[0].Note != want { t.Fatalf("note = %q, want %q", views[0].Note, want) } } // A number the list does not carry stays a number: a guessed backend would be // worse than saying nothing. func TestNoteOcppSourceSaysNothingWhenTheListCannotAnswer(t *testing.T) { views := []chargerDetailView{ {ID: "ocpp", Attrs: map[string]string{"source": "7"}}, {ID: "ocppEndpoints", Attrs: map[string]string{"list[0].source": "0", "list[0].url": "wss://x/0"}}, } noteOcppSource(views) if views[0].Note != "" { t.Fatalf("note = %q, want none", views[0].Note) } } // The entry's own address, not a neighbour's. func TestEndpointForSourceStaysWithinItsEntry(t *testing.T) { endpoints := map[string]string{ "list[0].source": "0", "list[0].endpoint": "wss://a", "list[1].source": "2", "list[1].endpoint": "wss://b", } if got := endpointForSource(endpoints, "2"); got != "wss://b" { t.Fatalf("endpointForSource = %q, want wss://b", got) } if got := endpointForSource(endpoints, "3"); got != "" { t.Fatalf("endpointForSource = %q, want empty", got) } } // The multi-serial views accept either spelling, and reject neither silently. func TestSerialList(t *testing.T) { if got := serialList(invokeParams{SN: "AT1"}); len(got) != 1 || got[0] != "AT1" { t.Fatalf("single serial = %v", got) } got := serialList(invokeParams{SN: "AT1", SNs: []string{"AT2", " ", "AT3"}}) if len(got) != 2 || got[0] != "AT2" || got[1] != "AT3" { t.Fatalf("several serials = %v", got) } if got := serialList(invokeParams{}); len(got) != 0 { t.Fatalf("nothing given = %v", got) } } // The paged and multi-view endpoints get a sane ask when the caller does not // care, rather than a zero the cloud reads as "no page at all". func TestPagingAndViewDefaults(t *testing.T) { if pageNum(0) != 1 || pageNum(3) != 3 { t.Fatalf("pageNum: %d %d", pageNum(0), pageNum(3)) } if pageSize(0) != 20 || pageSize(500) != 20 || pageSize(50) != 50 { t.Fatalf("pageSize: %d %d %d", pageSize(0), pageSize(500), pageSize(50)) } if upgradeRecordView(0) != 1 || upgradeRecordView(4) != 1 || upgradeRecordView(2) != 2 { t.Fatalf("upgradeRecordView: %d %d %d", upgradeRecordView(0), upgradeRecordView(4), upgradeRecordView(2)) } }