This should be the last of the API interactions we need. Signed-off-by: Yonah <contrib@gensokyo.uk>
118 lines
3.0 KiB
Go
118 lines
3.0 KiB
Go
package monstersirenfetch_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
. "git.gensokyo.uk/yonah/monstersirenfetch"
|
|
)
|
|
|
|
func TestNullableString(t *testing.T) {
|
|
checkJSONRoundTripM(t, []jsonRoundTripTestCase[NullableString]{
|
|
{"zero", nil, `null`, ""},
|
|
{"string", nil, `"null"`, "null"},
|
|
|
|
{"invalid json", newSyntaxError("unexpected end of JSON input", 5), `"null`, "\x00"},
|
|
})
|
|
}
|
|
|
|
func TestStringInt(t *testing.T) {
|
|
checkJSONRoundTripM(t, []jsonRoundTripTestCase[StringInt]{
|
|
{"valid long", nil, `"3735928559"`, 0xdeadbeef},
|
|
{"valid pad 4", nil, `"0000"`, 0},
|
|
{"valid 4", nil, `"1000"`, 1e3},
|
|
{"valid pad 6", nil, `"010000"`, 1e4},
|
|
{"valid 6", nil, `"100000"`, 1e5},
|
|
|
|
{"invalid json", newSyntaxError("unexpected end of JSON input", 11), `"3735928559`, -1},
|
|
{"invalid number", &strconv.NumError{Func: "Atoi", Num: ":3735928559", Err: strconv.ErrSyntax}, `":3735928559"`, -1},
|
|
})
|
|
}
|
|
|
|
func newSyntaxError(msg string, offset int64) *json.SyntaxError {
|
|
e := &json.SyntaxError{Offset: offset}
|
|
msgV := reflect.ValueOf(e).Elem().FieldByName("msg")
|
|
reflect.NewAt(msgV.Type(), unsafe.Pointer(msgV.UnsafeAddr())).Elem().SetString(msg)
|
|
return e
|
|
}
|
|
|
|
type jsonRoundTripTestCase[T comparable] struct {
|
|
name string
|
|
wantErr error
|
|
data string
|
|
val T
|
|
}
|
|
|
|
func checkJSONRoundTripM[T comparable](t *testing.T, testCases []jsonRoundTripTestCase[T]) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.wantErr == nil {
|
|
t.Run("marshal", func(t *testing.T) {
|
|
if got, err := json.Marshal(&tc.val); err != nil {
|
|
t.Fatalf("Marshal: error = %v", err)
|
|
} else if string(got) != tc.data {
|
|
t.Errorf("Marshal: %s, want %s", string(got), tc.data)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("unmarshal", func(t *testing.T) {
|
|
var got T
|
|
err := json.Unmarshal([]byte(tc.data), &got)
|
|
|
|
if !reflect.DeepEqual(err, tc.wantErr) {
|
|
t.Errorf("Unmarshal: error = %v, want %v", err, tc.wantErr)
|
|
}
|
|
|
|
if tc.wantErr == nil {
|
|
if got != tc.val {
|
|
t.Errorf("Unmarshal: %v, want %v", got, tc.val)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func checkJSONRoundTrip[T any](t *testing.T, v T, data []byte) {
|
|
t.Run("marshal", func(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
buf.Grow(len(data))
|
|
e := json.NewEncoder(buf)
|
|
// to match upstream behaviour
|
|
e.SetEscapeHTML(false)
|
|
err := e.Encode(&v)
|
|
if err != nil {
|
|
t.Fatalf("Marshal: error = %v", err)
|
|
}
|
|
got := buf.Bytes()[:buf.Len()-1]
|
|
if string(got) != string(data) {
|
|
t.Errorf("Marshal:\n%s\nwant\n%s", string(got), string(data))
|
|
}
|
|
})
|
|
|
|
t.Run("unmarshal", func(t *testing.T) {
|
|
var got T
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatalf("Unmarshal: error = %v", err)
|
|
}
|
|
if !reflect.DeepEqual(&got, &v) {
|
|
t.Errorf("Unmarshal:\n%#v\nwant\n%#v", got, v)
|
|
}
|
|
})
|
|
}
|
|
|
|
type errorCloser struct{ io.Reader }
|
|
|
|
func (errorCloser) Close() error { return os.ErrInvalid }
|
|
|
|
type nopCloser struct{ io.Reader }
|
|
|
|
func (nopCloser) Close() error { return nil }
|