package extension_test import ( "errors" "slices" "testing" datekeys "g.activething.com/go/DateKeys" "g.activething.com/go/DateKeys/extension" "g.activething.com/go/DateKeys/internal/cbortest" ) // Spec §54: the elements of an extension array are in strictly ascending // order of the UTF-8 bytes of extension_id, compared as unsigned bytes, a // proper prefix first; never by UTF-16 code units or a locale collation. The // strict order also forbids a repeated extension_id. func TestOrderIsUnsignedBytewise(t *testing.T) { in := []extension.Extension{} for _, id := range []string{"\U00010000", "。", "é", "z", "Z", "a\x00", "a"} { in = append(in, extension.Extension{ID: id, Version: 1}) } got, err := extension.Canonical(in) if err != nil { t.Fatal(err) } var order []string for _, e := range got { order = append(order, e.ID) } // 5a, 61, 61 00, 7a, c3 a9, ef bd a1, f0 90 80 80. if want := []string{"Z", "a", "a\x00", "z", "é", "。", "\U00010000"}; !slices.Equal(order, want) { t.Fatalf("order %q, want %q", order, want) } for _, tc := range []struct { name string ids []string ok bool }{ {"U+FF61 before U+10000, by UTF-8 bytes", []string{"。", "\U00010000"}, true}, {"U+10000 before U+FF61, by UTF-16 code units", []string{"\U00010000", "。"}, false}, {"a proper prefix first", []string{"a", "ab"}, true}, {"a proper prefix last", []string{"ab", "a"}, false}, {"byte 7a before c3, unsigned", []string{"z", "é"}, true}, {"byte c3 before 7a, signed", []string{"é", "z"}, false}, {"uppercase before lowercase", []string{"B", "a"}, true}, {"case-insensitive collation", []string{"a", "B"}, false}, {"repeated", []string{"a", "a"}, false}, } { arr := make([]any, len(tc.ids)) for i, id := range tc.ids { arr[i] = map[uint64]any{0: id, 1: uint64(1)} } b, err := cbortest.Marshal(arr) if err != nil { t.Fatal(err) } _, err = decodeArray(b) if tc.ok && err != nil || !tc.ok && !errors.Is(err, datekeys.ErrNonCanonicalCBOR) { t.Errorf("%s: %v", tc.name, err) } } }