package macro import ( "reflect" "strconv" "testing" "time" ) // Most important tests to look: // ../parser/parser_test.go // ../lexer/lexer_test.go func TestGoodParamFunc(t *testing.T) { good1 := func(min int, max int) func(string) bool { return func(paramValue string) bool { return true } } good2 := func(min uint64, max uint64) func(string) bool { return func(paramValue string) bool { return true } } notgood1 := func(min int, max int) bool { return false } if !goodParamFunc(reflect.TypeOf(good1)) { t.Fatalf("expected good1 func to be good but it's not") } if !goodParamFunc(reflect.TypeOf(good2)) { t.Fatalf("expected good2 func to be good but it's not") } if goodParamFunc(reflect.TypeOf(notgood1)) { t.Fatalf("expected notgood1 func to be the worst") } } func TestGoodParamFuncName(t *testing.T) { tests := []struct { name string good bool }{ {"range", true}, {"_range", true}, {"range_", true}, {"r_ange", true}, // numbers or other symbols are invalid. {"range1", false}, {"2range", false}, {"r@nge", false}, {"rang3", false}, } for i, tt := range tests { isGood := goodParamFuncName(tt.name) if tt.good && !isGood { t.Fatalf("tests[%d] - expecting valid name but got invalid for name %s", i, tt.name) } else if !tt.good && isGood { t.Fatalf("tests[%d] - expecting invalid name but got valid for name %s", i, tt.name) } } } func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, expectedType reflect.Kind, pass bool, i int) { t.Helper() if macroEvaluator.Evaluator == nil && pass { return // if not evaluator defined then it should allow everything. } value, passed := macroEvaluator.Evaluator(input) if pass != passed { t.Fatalf("%s - tests[%d] - expecting[pass] %v but got %v", t.Name(), i, pass, passed) } if !passed { return } if value == nil && expectedType != reflect.Invalid { t.Fatalf("%s - tests[%d] - expecting[value] to not be nil", t.Name(), i) } if v := reflect.ValueOf(value); v.Kind() != expectedType { t.Fatalf("%s - tests[%d] - expecting[value.Kind] %v but got %v", t.Name(), i, expectedType, v.Kind()) } } func TestStringEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "astring"}, // 0 {true, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "main.css"}, // 3 {true, "/assets/main.css"}, // 4 // false never } // 0 for i, tt := range tests { testEvaluatorRaw(t, String, tt.input, reflect.String, tt.pass, i) } } func TestIntEvaluatorRaw(t *testing.T) { x64 := strconv.IntSize == 64 tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {x64, "9223372036854775807" /* max int64 */}, // 3 {x64, "-9223372036854775808" /* min int64 */}, // 4 {false, "-18446744073709553213213213213213121615"}, // 5 {false, "42 18446744073709551615"}, // 6 {false, "--42"}, // 7 {true, "+42"}, // 8 {false, "main.css"}, // 9 {false, "/assets/main.css"}, // 10 } for i, tt := range tests { testEvaluatorRaw(t, Int, tt.input, reflect.Int, tt.pass, i) } } func BenchmarkIntEvaluatorRaw(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { Int.Evaluator("1234568320") Int.Evaluator("-12345678999321") } } func TestInt8EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {false, "32321"}, // 2 {true, "127" /* max int8 */}, // 3 {true, "-128" /* min int8 */}, // 4 {false, "128"}, // 5 {false, "-129"}, // 6 {false, "-18446744073709553213213213213213121615"}, // 7 {false, "42 18446744073709551615"}, // 8 {false, "--42"}, // 9 {true, "+42"}, // 10 {false, "main.css"}, // 11 {false, "/assets/main.css"}, // 12 } for i, tt := range tests { testEvaluatorRaw(t, Int8, tt.input, reflect.Int8, tt.pass, i) } } func TestInt16EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "32767" /* max int16 */}, // 3 {true, "-32768" /* min int16 */}, // 4 {false, "-32769"}, // 5 {false, "32768"}, // 6 {false, "-18446744073709553213213213213213121615"}, // 7 {false, "42 18446744073709551615"}, // 8 {false, "--42"}, // 9 {true, "+42"}, // 10 {false, "main.css"}, // 11 {false, "/assets/main.css"}, // 12 } for i, tt := range tests { testEvaluatorRaw(t, Int16, tt.input, reflect.Int16, tt.pass, i) } } func TestInt32EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "1"}, // 3 {true, "42"}, // 4 {true, "2147483647" /* max int32 */}, // 5 {true, "-2147483648" /* min int32 */}, // 6 {false, "-2147483649"}, // 7 {false, "2147483648"}, // 8 {false, "-18446744073709553213213213213213121615"}, // 9 {false, "42 18446744073709551615"}, // 10 {false, "--42"}, // 11 {true, "+42"}, // 12 {false, "main.css"}, // 13 {false, "/assets/main.css"}, // 14 } for i, tt := range tests { testEvaluatorRaw(t, Int32, tt.input, reflect.Int32, tt.pass, i) } } func TestInt64EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {false, "18446744073709551615"}, // 2 {false, "92233720368547758079223372036854775807"}, // 3 {false, "9223372036854775808 9223372036854775808"}, // 4 {false, "main.css"}, // 5 {false, "/assets/main.css"}, // 6 {true, "9223372036854775807"}, // 7 {true, "-9223372036854775808"}, // 8 {true, "-0"}, // 9 {true, "1"}, // 10 {true, "-042"}, // 11 {true, "142"}, // 12 } for i, tt := range tests { testEvaluatorRaw(t, Int64, tt.input, reflect.Int64, tt.pass, i) } } func TestUintEvaluatorRaw(t *testing.T) { x64 := strconv.IntSize == 64 tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "1"}, // 3 {true, "42"}, // 4 {x64, "18446744073709551615" /* max uint64 */}, // 5 {true, "4294967295" /* max uint32 */}, // 6 {false, "-2147483649"}, // 7 {true, "2147483648"}, // 8 {false, "-18446744073709553213213213213213121615"}, // 9 {false, "42 18446744073709551615"}, // 10 {false, "--42"}, // 11 {false, "+42"}, // 12 {false, "main.css"}, // 13 {false, "/assets/main.css"}, // 14 } for i, tt := range tests { testEvaluatorRaw(t, Uint, tt.input, reflect.Uint, tt.pass, i) } } func TestUint8EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {false, "-9223372036854775808"}, // 2 {false, "main.css"}, // 3 {false, "/assets/main.css"}, // 4 {false, "92233720368547758079223372036854775807"}, // 5 {false, "9223372036854775808 9223372036854775808"}, // 6 {false, "-1"}, // 7 {false, "-0"}, // 8 {false, "+1"}, // 9 {false, "18446744073709551615"}, // 10 {false, "9223372036854775807"}, // 11 {true, "021"}, // 12 - leading zeroes are allowed. {false, "300"}, // 13 {true, "0"}, // 14 {true, "255"}, // 15 {true, "21"}, // 16 } for i, tt := range tests { testEvaluatorRaw(t, Uint8, tt.input, reflect.Uint8, tt.pass, i) } } func TestUint16EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "65535" /* max uint16 */}, // 3 {true, "0" /* min uint16 */}, // 4 {false, "-32769"}, // 5 {true, "32768"}, // 6 {false, "-18446744073709553213213213213213121615"}, // 7 {false, "42 18446744073709551615"}, // 8 {false, "--42"}, // 9 {false, "+42"}, // 10 {false, "main.css"}, // 11 {false, "/assets/main.css"}, // 12 } for i, tt := range tests { testEvaluatorRaw(t, Uint16, tt.input, reflect.Uint16, tt.pass, i) } } func TestUint32EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "1"}, // 3 {true, "42"}, // 4 {true, "4294967295" /* max uint32*/}, // 5 {true, "0" /* min uint32 */}, // 6 {false, "-2147483649"}, // 7 {true, "2147483648"}, // 8 {false, "-18446744073709553213213213213213121615"}, // 9 {false, "42 18446744073709551615"}, // 10 {false, "--42"}, // 11 {false, "+42"}, // 12 {false, "main.css"}, // 13 {false, "/assets/main.css"}, // 14 } for i, tt := range tests { testEvaluatorRaw(t, Uint32, tt.input, reflect.Uint32, tt.pass, i) } } func TestUint64EvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {false, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {false, "-9223372036854775808"}, // 2 {false, "main.css"}, // 3 {false, "/assets/main.css"}, // 4 {false, "92233720368547758079223372036854775807"}, // 5 {false, "9223372036854775808 9223372036854775808"}, // 6 {false, "-1"}, // 7 {false, "-0"}, // 8 {false, "+1"}, // 9 {true, "18446744073709551615"}, // 10 {true, "9223372036854775807"}, // 11 {true, "0"}, // 12 } for i, tt := range tests { testEvaluatorRaw(t, Uint64, tt.input, reflect.Uint64, tt.pass, i) } } func TestAlphabeticalEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {false, "32321"}, // 2 {false, "main.css"}, // 3 {false, "/assets/main.css"}, // 4 } for i, tt := range tests { testEvaluatorRaw(t, Alphabetical, tt.input, reflect.String, tt.pass, i) } } func TestFileEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "astring"}, // 0 {false, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "main.css"}, // 3 {false, "/assets/main.css"}, // 4 } for i, tt := range tests { testEvaluatorRaw(t, File, tt.input, reflect.String, tt.pass, i) } } func TestPathEvaluatorRaw(t *testing.T) { pathTests := []struct { pass bool input string }{ {true, "astring"}, // 0 {true, "astringwith_numb3rS_and_symbol$"}, // 1 {true, "32321"}, // 2 {true, "main.css"}, // 3 {true, "/assets/main.css"}, // 4 {true, "disk/assets/main.css"}, // 5 } for i, tt := range pathTests { testEvaluatorRaw(t, Path, tt.input, reflect.String, tt.pass, i) } } func TestUUIDEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "978ad967-5fad-4c82-af99-580097ace662"}, // v4 {true, "c7067f9c-6d43-11eb-9439-0242ac130002"}, // v1 {false, "astring"}, // 2 {false, "astringwith_numb3rS_and_symbol$"}, // 3 {false, "32321"}, // 4 {false, "main.css"}, // 5 {false, "/assets/main.css"}, // 6 } for i, tt := range tests { testEvaluatorRaw(t, UUID, tt.input, reflect.String, tt.pass, i) } } func TestMailEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "kataras2006@hotmail.com"}, // 0 {true, "iris-go@outlook.com"}, // 1 {true, "iris-go@mail"}, // 2 {true, "kataras@k.c"}, // 3 {false, "www.kataras@"}, // 4 {false, "name"}, // 5 {false, "b-c@"}, // 6 } for i, tt := range tests { testEvaluatorRaw(t, Mail, tt.input, reflect.String, tt.pass, i) } } func TestEmailEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string }{ {true, "kataras2006@hotmail.com"}, // 0 {true, "iris-go@outlook.com"}, // 1 {false, "iris-go@mail"}, // 2 {false, "kataras@k.c"}, // 3 {false, "www.kataras@"}, // 4 {false, "name"}, // 5 {false, "b-c@"}, // 6 } for i, tt := range tests { testEvaluatorRaw(t, Email, tt.input, reflect.String, tt.pass, i) } } func TestDateEvaluatorRaw(t *testing.T) { tests := []struct { pass bool input string timeStringValue string }{ {true, "2022/04/21", "2022-04-21 00:00:00 +0000 UTC"}, // 0 {true, "2022/12/05", "2022-12-05 00:00:00 +0000 UTC"}, // 1 {false, "2022/4", ""}, // 2 {false, "1/4/1", ""}, // 3 {false, "2022/4/", ""}, // 4 {false, "2022/4/21/0", ""}, // 5 {false, "1993", ""}, // 6 } for i, tt := range tests { testEvaluatorRaw(t, Date, tt.input, reflect.TypeOf(time.Time{}).Kind(), tt.pass, i) if v, ok := Date.Evaluator(tt.input); ok { if value, ok := v.(time.Time); ok { if expected, got := tt.timeStringValue, value.String(); expected != got { t.Fatalf("[%d] expected: %s but got: %s", i, expected, got) } } else { t.Fatalf("[%d] expected to be able to cast as time.Time directly", i) } } } } func TestConvertBuilderFunc(t *testing.T) { fn := func(min uint64, slice []string) func(string) bool { return func(paramValue string) bool { if expected, got := "ok", paramValue; expected != got { t.Fatalf("paramValue is not the expected one: %s vs %s", expected, got) } if expected, got := uint64(1), min; expected != got { t.Fatalf("min argument is not the expected one: %d vs %d", expected, got) } if expected, got := []string{"name1", "name2"}, slice; len(expected) == len(got) { if expected, got := "name1", slice[0]; expected != got { t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 0, expected, got) } if expected, got := "name2", slice[1]; expected != got { t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 1, expected, got) } } else { t.Fatalf("slice argument is not the expected one, the length is difference: %d vs %d", len(expected), len(got)) } return true } } evalFunc := convertBuilderFunc(fn) if !evalFunc([]string{"1", "[name1,name2]"}).Call([]reflect.Value{reflect.ValueOf("ok")})[0].Interface().(bool) { t.Fatalf("failed, it should fail already") } fnSimplify := func(requestParamValue string) bool { return requestParamValue == "kataras" } evalFunc = convertBuilderFunc(fnSimplify) if !evalFunc([]string{}).Call([]reflect.Value{reflect.ValueOf("kataras")})[0].Interface().(bool) { t.Fatalf("it should pass, the combile arguments are empty and the given request value is the expected one") } defer func() { if r := recover(); r == nil { t.Fatalf("it should panic, the combile arguments are more than one") } }() // should panic. evalFunc([]string{"1"}).Call([]reflect.Value{reflect.ValueOf("kataras")}) }