package ident_test import ( "encoding" "encoding/base64" "fmt" "reflect" "strings" "testing" "time" "git.gensokyo.uk/cofront/cof-spec/ident" ) // rTestCases describes multiple representation test cases. type rTestCases[V any, S interface { encoding.TextMarshaler encoding.TextUnmarshaler fmt.Stringer *V }] []struct { name string ident V want []byte err error } // checkRepresentation runs tests described by testCases. func (testCases rTestCases[V, S]) run(t *testing.T) { t.Helper() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Helper() t.Parallel() t.Run("decode", func(t *testing.T) { t.Helper() t.Parallel() var got V if err := S(&got).UnmarshalText(tc.want); !reflect.DeepEqual(err, tc.err) { t.Fatalf("UnmarshalText: error = %v, want %v", err, tc.err) } if tc.err != nil { return } if !reflect.DeepEqual(got, tc.ident) { t.Errorf("UnmarshalText: %#v, want %#v", got, tc.ident) } }) if tc.err != nil { return } t.Run("encode", func(t *testing.T) { t.Helper() t.Parallel() if got, err := S(&tc.ident).MarshalText(); err != nil { t.Fatalf("MarshalText: error = %v", err) } else if string(got) != string(tc.want) { raw, decodeErr := base64.URLEncoding.AppendDecode(nil, got) t.Logf("AppendDecode: %#v, error = %v", raw, decodeErr) t.Errorf("MarshalText: %#v, want %#v", got, tc.want) } if got := S(&tc.ident).String(); got != string(tc.want) { t.Errorf("String: %s, want %s", got, tc.want) } }) }) } } func TestFS(t *testing.T) { t.Parallel() rTestCases[ident.F[ident.S, *ident.S], *ident.F[ident.S, *ident.S]]{ {"separator", ident.F[ident.S, *ident.S]{}, nil, ident.ErrSeparator}, {"bad ident", ident.F[ident.S, *ident.S]{}, []byte( strings.Repeat("=", ident.EncodedSizeSystem) + ":", ), base64.CorruptInputError(0)}, {"valid", ident.F[ident.S, *ident.S]{ I: &ident.S{ Site: ident.TrivialSite, Host: ident.TrivialHost, Time: uint64(time.Date( 0xfd, 7, 15, 23, 59, 59, 0xcafe, time.UTC, ).UnixNano()), ID: 0xfee1dead0badf00d, }, Remote: mustNewRemote("gensokyo.uk"), }, []byte("_srt_r66_sr-AEPO8ZJKEA3wrQut3uH-:gensokyo.uk"), nil}, }.run(t) } func TestFM(t *testing.T) { t.Parallel() rTestCases[ident.F[ident.M, *ident.M], *ident.F[ident.M, *ident.M]]{ {"separator", ident.F[ident.M, *ident.M]{}, nil, ident.ErrSeparator}, {"bad ident", ident.F[ident.M, *ident.M]{}, []byte( strings.Repeat("=", ident.EncodedSizeMember) + ":", ), base64.CorruptInputError(0)}, {"valid", ident.F[ident.M, *ident.M]{ I: &ident.M{ PartM: ident.PartM{ Serial: 0xfdfdfdfdfdfdfdfd, Time: uint64(time.Date( 0xfd, 7, 15, 23, 59, 59, 0xcab, time.UTC, ).UnixNano()), ID: 0x2e736e64, }, System: ident.S{ Site: ident.TrivialSite, Host: ident.TrivialHost, Time: uint64(time.Date( 0xfd, 7, 15, 23, 59, 59, 0xcafe, time.UTC, ).UnixNano()), ID: 0xfee1dead0badf00d, }, }, Remote: mustNewRemote("gensokyo.uk"), }, []byte("_f39_f39_f2rQkLO8ZJKEGRucy4AAAAA_srt_r66_sr-AEPO8ZJKEA3wrQut3uH-:gensokyo.uk"), nil}, }.run(t) } func TestErrors(t *testing.T) { t.Parallel() testCases := []struct { name string err error want string }{ {"UnexpectedSizeError", &ident.UnexpectedSizeError{ Data: make([]byte, 1<<10), Want: ident.EncodedSizeSystem, }, "got 1024 bytes for a 32-byte identifier"}, {"InvalidLabelLDH start", &ident.InvalidLabelError{ Data: []byte{'-'}, Label: 0xbad, Index: 0, Reason: ident.InvalidLabelLDH, }, "label 2989 starts with '-'"}, {"InvalidLabelLDH end", &ident.InvalidLabelError{ Data: []byte{0, '-'}, Label: 0xbad, Index: 1, Reason: ident.InvalidLabelLDH, }, "label 2989 ends with '-'"}, {"InvalidLabelLDH", &ident.InvalidLabelError{ Data: []byte{0}, Label: 0xbad, Index: 0, Reason: ident.InvalidLabelLDH, }, `label 2989 contains invalid byte '\x00' at index 0`}, {"InvalidLabelShort", &ident.InvalidLabelError{ Label: 0xf00d, Reason: ident.InvalidLabelShort, }, "label 61453 is empty"}, {"InvalidLabelLong", &ident.InvalidLabelError{ Label: 0xf00d, Reason: ident.InvalidLabelLong, }, "label 61453 is longer than 63 bytes"}, {"InvalidLabelError", &ident.InvalidLabelError{ Label: 0xcafe, Reason: 0xbadf00d, }, "invalid label 51966 at byte 0"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() if got := tc.err.Error(); got != tc.want { t.Errorf("Error: %q, want %q", got, tc.want) } }) } }