You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
3.2 KiB
86 lines
3.2 KiB
package testkit
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
"g.activething.com/go/DateKeys/extension"
|
|
)
|
|
|
|
// NoteVectorFile is testdata/vectors/note.json: the data of the public note
|
|
// (spec v0.11, §24.1), the extension datekeys.note of PUBLIC_HEADER, and what
|
|
// a writer and a reader do with it. A writer writes a note only when Result
|
|
// is ResultOK, and otherwise refuses it with Detail; a reader shows it only
|
|
// then, and otherwise treats it as unusable and says so.
|
|
type NoteVectorFile struct {
|
|
Spec string `json:"spec"`
|
|
Description string `json:"description"`
|
|
Notes []NoteVector `json:"notes"`
|
|
}
|
|
|
|
// NoteVector is the data of a note in hexadecimal, its result and, when it
|
|
// fails, the text of the rule it breaks, without its prefix and its code.
|
|
type NoteVector struct {
|
|
Name string `json:"name"`
|
|
Data string `json:"data"`
|
|
Result string `json:"result"`
|
|
Detail string `json:"detail,omitempty"`
|
|
}
|
|
|
|
// NoteResult checks the data of a note with the rules of §24.1, and returns
|
|
// its result and the detail of a failure.
|
|
func NoteResult(data []byte) (string, string) {
|
|
err := extension.CheckNote(string(data))
|
|
if err == nil {
|
|
return ResultOK, ""
|
|
}
|
|
if !errors.Is(err, datekeys.ErrExtensionDataInvalid) {
|
|
return Result(err), err.Error()
|
|
}
|
|
return Result(err), strings.TrimSuffix(err.Error(), ": "+datekeys.ErrExtensionDataInvalid.Code())
|
|
}
|
|
|
|
// NoteVectors computes testdata/vectors/note.json, and fails if a vector does
|
|
// not get the result it is written for.
|
|
func NoteVectors() (NoteVectorFile, error) {
|
|
f := NoteVectorFile{
|
|
Spec: SpecVersion,
|
|
Description: "The data of the public note, datekeys.note version 1 in the noncritical array of PUBLIC_HEADER (spec §24.1): " +
|
|
"the text in UTF-8, from 1 to 1024 bytes, that meets the rules of the declared author of §29.6. See testdata/README.md.",
|
|
}
|
|
bad := datekeys.ErrExtensionDataInvalid.Code()
|
|
var errs []error
|
|
for _, c := range []struct {
|
|
name string
|
|
data []byte
|
|
want string
|
|
}{
|
|
{"a note", []byte("Cartas del viaje a Lisboa"), ResultOK},
|
|
{"letters that are not ASCII and an emoji", []byte("Ñandú, año 2026 \U0001F30D"), ResultOK},
|
|
{"1024 bytes", []byte(strings.Repeat("a", 1024)), ResultOK},
|
|
{"an emoji with VS16, which the whitelist admits", []byte("\u2764\ufe0f"), ResultOK},
|
|
{"no byte", []byte{}, bad},
|
|
{"1025 bytes", []byte(strings.Repeat("a", 1025)), bad},
|
|
{"a tab", []byte("a\tb"), bad},
|
|
{"a line feed", []byte("a\nb"), bad},
|
|
{"a space at the start", []byte(" a"), bad},
|
|
{"a space at the end", []byte("a "), bad},
|
|
{"a bidi override", []byte("a\u202eb"), bad},
|
|
{"a zero width space, ignorable", []byte("a\u200bb"), bad},
|
|
{"a byte order mark at the start", []byte("\ufeffHola"), bad},
|
|
{"a byte that is not UTF-8", []byte{'a', 0xff}, bad},
|
|
{"the UTF-8 of a lone surrogate", []byte{0xed, 0xa0, 0x80}, bad},
|
|
{"a noncharacter", []byte("a\ufffeb"), bad},
|
|
} {
|
|
got, detail := NoteResult(c.data)
|
|
if got != c.want {
|
|
errs = append(errs, fmt.Errorf("note %q: %s, want %s", c.name, got, c.want))
|
|
}
|
|
f.Notes = append(f.Notes, NoteVector{Name: c.name, Data: hex.EncodeToString(c.data), Result: got, Detail: detail})
|
|
}
|
|
return f, errors.Join(errs...)
|
|
}
|