package byd // Expected values come from pybyd 0.0.75 — regenerate with testdata/gen_fixtures.py. import ( "strings" "testing" ) func TestHashing(t *testing.T) { tests := []struct { name string got string want string }{ {"md5Hex", md5Hex("hunter2"), "2AB96390C7DBE3439DE74D0C9B0B1767"}, {"pwdLoginKey", pwdLoginKey("hunter2"), "38075EADB88F8484DDC00C4D272D9F4C"}, {"sha1Mixed", sha1Mixed("abc"), "A9993E36476816aBA3e25717850C26c9Cd0D89d"}, } for _, tt := range tests { if tt.got != tt.want { t.Errorf("%s = %s, want %s", tt.name, tt.got, tt.want) } } } // TestSha1MixedDropsZeros covers the odd half of the algorithm. Note the // filtering is indexed against the pre-filter string, not the result, so the // output can still contain '0' at an even position — the only thing guaranteed // is that it comes out shorter than a plain 40-char hex digest. func TestSha1MixedDropsZeros(t *testing.T) { got := sha1Mixed("abc") if len(got) >= 40 { t.Errorf("sha1Mixed(%q) = %q (%d chars); zero filtering did not run", "abc", got, len(got)) } if strings.ToLower(got) == got || strings.ToUpper(got) == got { t.Errorf("sha1Mixed(%q) = %q; expected mixed case", "abc", got) } } func TestJmapPreservesInsertionOrder(t *testing.T) { // A Go map would sort these keys and change the checkcode, so this is the // guard on the ordered marshalling the whole protocol depends on. m := jmap{} m.set("zeta", "1") m.set("alpha", "2") b, err := marshalCompact(m) if err != nil { t.Fatal(err) } if want := `{"zeta":"1","alpha":"2"}`; string(b) != want { t.Fatalf("marshalCompact = %s, want %s", b, want) } got, err := checkcode(m) if err != nil { t.Fatal(err) } if want := "3d6e01c0671c8e4d2e2fbda3466886b0"; got != want { t.Errorf("checkcode = %s, want %s", got, want) } } func TestJmapSetOverwritesInPlace(t *testing.T) { m := jmap{} m.set("a", "1") m.set("b", "2") m.set("a", "3") // must update, not append, or the order shifts b, _ := marshalCompact(m) if want := `{"a":"3","b":"2"}`; string(b) != want { t.Errorf("marshalCompact = %s, want %s", b, want) } } func TestJmapCloneIsIndependent(t *testing.T) { orig := jmap{} orig.set("a", "1") clone := orig.clone() clone.set("b", "2") clone.set("a", "changed") if orig.get("a") != "1" || len(orig) != 1 { t.Errorf("clone mutated the original: %v", orig) } } // TestMarshalCompactDoesNotEscapeHTML guards a real difference between Go and // Python: encoding/json escapes < > & by default, which would change every // checkcode for a payload containing them. func TestMarshalCompactDoesNotEscapeHTML(t *testing.T) { m := jmap{} m.set("q", "ad") b, err := marshalCompact(m) if err != nil { t.Fatal(err) } if want := `{"q":"ad"}`; string(b) != want { t.Errorf("marshalCompact = %s, want %s", b, want) } } func TestBuildSignStringSortsKeys(t *testing.T) { m := jmap{} m.set("b", "2") m.set("a", "1") m.set("c", "3") if got, want := buildSignString(m, "pw"), "a=1&b=2&c=3&password=pw"; got != want { t.Errorf("buildSignString = %q, want %q", got, want) } } func TestAESRoundTrip(t *testing.T) { key := pwdLoginKey("hunter2") const plain = `{"a":1,"b":"ü"}` ct, err := aesEncryptHex(plain, key) if err != nil { t.Fatal(err) } got, err := aesDecryptUTF8(ct, key) if err != nil { t.Fatal(err) } if got != plain { t.Errorf("round trip = %q, want %q", got, plain) } } func TestAESRejectsBadInput(t *testing.T) { key := pwdLoginKey("hunter2") if _, err := aesEncryptHex("x", "not-hex"); err == nil { t.Error("expected an error for a non-hex key") } if _, err := aesEncryptHex("x", "AABB"); err == nil { t.Error("expected an error for a 2-byte key") } if _, err := aesDecryptUTF8("zz", key); err == nil { t.Error("expected an error for non-hex ciphertext") } if _, err := aesDecryptUTF8("AABB", key); err == nil { t.Error("expected an error for a partial block") } }