package ankersolix import ( "context" "encoding/json" "strings" "testing" ) // A card number typed with punctuation is the same card as the one the account // stores bare — anything else deletes nothing and adds a duplicate. func TestNormalizeCardNumber(t *testing.T) { for _, tc := range []struct{ in, want string }{ {"4754DC2A", "4754DC2A"}, {"47:54:dc:2a", "4754DC2A"}, {" 47-54 dc 2a ", "4754DC2A"}, {"04dfe672151a90", "04DFE672151A90"}, {"", ""}, {" :- ", ""}, } { if got := normalizeCardNumber(tc.in); got != tc.want { t.Errorf("normalizeCardNumber(%q) = %q, want %q", tc.in, got, tc.want) } } } // The name a card gets when it is added without one, in the shape the Anker app // writes so a card added here does not stand out in it. func TestRfidCardLabel(t *testing.T) { if got := rfidCardLabel("4754DC2A"); got != "RFID DC2A" { t.Errorf("rfidCardLabel = %q, want %q", got, "RFID DC2A") } if got := rfidCardLabel("2A"); got != "RFID 2A" { t.Errorf("short number: rfidCardLabel = %q, want %q", got, "RFID 2A") } } func TestParseRfidCards(t *testing.T) { body := []byte(`{"code":0,"data":{"list":[ {"alias_name":"RFID DC2A","card_number":"4754DC2A","create_time":1788187299}, {"alias_name":"RFID B93F","card_number":"DA7CB93F","create_time":1788187315}]}}`) cards, err := parseRfidCards(body) if err != nil { t.Fatalf("parseRfidCards: %v", err) } if len(cards) != 2 { t.Fatalf("got %d cards, want 2", len(cards)) } if cards[0].Name != "RFID DC2A" || cards[0].Number != "4754DC2A" { t.Fatalf("first card = %+v", cards[0]) } if string(cards[0].Added) != "1788187299" { t.Fatalf("create_time = %q, want it relayed as it arrived", cards[0].Added) } // A charger with no cards answers with an empty document, which is an answer // and not a failure. cards, err = parseRfidCards([]byte(`{"code":0,"data":{}}`)) if err != nil || len(cards) != 0 { t.Fatalf("empty list: got %d cards, err %v", len(cards), err) } } // Presence is what a write is judged by, and it is judged on the number rather // than on how either side punctuated it. func TestCardPresent(t *testing.T) { cards := []rfidCard{{Number: "4754DC2A"}, {Number: "DA7CB93F"}} if !cardPresent(cards, "47:54:dc:2a") { t.Error("punctuated number: want present") } if cardPresent(cards, "DEADBEEF") { t.Error("unknown number: want absent") } if cardPresent(nil, "4754DC2A") { t.Error("empty charger: want absent") } } // Neither write may reach the cloud without knowing which charger and which // card: an endpoint nobody has documented is not one to send half a request to. func TestCardWritesRefuseIncompleteRequests(t *testing.T) { p := &Plugin{} _ = p.Init(context.Background(), map[string]string{"email": "u@example.com", "password": "p"}) for _, action := range []string{"rfid-card-save", "rfid-card-delete"} { if _, err := p.Invoke(context.Background(), action, nil); err == nil || !strings.Contains(err.Error(), "requires an sn") { t.Errorf("%s without a serial: got %v, want an sn-required error", action, err) } // A serial but no card, and no card number to be found in punctuation // either: both stop before anything is sent. for _, params := range []string{`{"sn":"EVSN1"}`, `{"sn":"EVSN1","cardNumber":" :- "}`} { _, err := p.Invoke(context.Background(), action, json.RawMessage(params)) if err == nil || !strings.Contains(err.Error(), "card number is required") { t.Errorf("%s with %s: got %v, want a card-number-required error", action, params, err) } } } }