diff --git a/containers/queues_test.go b/containers/queues_test.go new file mode 100644 index 0000000..997a407 --- /dev/null +++ b/containers/queues_test.go @@ -0,0 +1,95 @@ +package containers_test + +import ( + "testing" + + "git.mstar.dev/mstar/goutils/containers" + "git.mstar.dev/mstar/goutils/other" +) + +func TestQueue(t *testing.T) { + queue := containers.BuildQueue[int]() + elem, err := queue.Top() + switch err { + case containers.ErrEmptyQueue: + // Expected, continue + case containers.ErrInvalidQueue: + t.Fatal("New queue is invalid") + case nil: + t.Fatalf("New queue shouldn't have any elements yet: %v", elem) + } + elem, err = queue.Pop() + switch err { + case containers.ErrEmptyQueue: + // Expected, continue + case containers.ErrInvalidQueue: + t.Fatal("New queue is invalid") + case nil: + t.Fatalf("New queue shouldn't have any elements yet: %v", elem) + } + if !queue.IsEmpty() { + t.Fatal("New queue isn't empty") + } + + if err = queue.Push(other.IntoPointer(10)); err != nil { + t.Fatal("Failed to push value onto new queue") + } + if queue.IsEmpty() { + t.Fatal("Queue is empty after pushing a value") + } + + if err = queue.Push(other.IntoPointer(20)); err != nil { + t.Fatal("Failed to push value onto queue") + } + + elem, err = queue.Top() + if err != nil { + t.Fatalf("Failed to get top element from queue after inserting one: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &10") + case *elem == 10: + // Expected, continue + default: + t.Fatalf("Got %v; want &10", *elem) + } + + if queue.IsEmpty() { + t.Fatal("Top shouldn't pop an element from the queue") + } + + elem, err = queue.Pop() + if err != nil { + t.Fatalf("Expected no error while popping a value: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &10") + case *elem == 10: + // Expected, continue + default: + t.Fatalf("Got %v; want &10", *elem) + } + + if queue.IsEmpty() { + t.Fatal("Queue should still have one element left") + } + + elem, err = queue.Pop() + if err != nil { + t.Fatalf("Expected no error while popping a value: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &20") + case *elem == 20: + // Expected, continue + default: + t.Fatalf("Got %v; want &20", *elem) + } + + if !queue.IsEmpty() { + t.Fatal("Queue should be empty now") + } +} diff --git a/containers/stacks_test.go b/containers/stacks_test.go new file mode 100644 index 0000000..7afc85a --- /dev/null +++ b/containers/stacks_test.go @@ -0,0 +1,108 @@ +package containers_test + +import ( + "testing" + + "git.mstar.dev/mstar/goutils/containers" + "git.mstar.dev/mstar/goutils/other" +) + +func TestStack(t *testing.T) { + stack := containers.BuildStack[int]() + elem, err := stack.Top() + switch err { + case containers.ErrEmptyQueue: + // Expected, continue + case containers.ErrInvalidQueue: + t.Fatal("New queue is invalid") + case nil: + t.Fatalf("New queue shouldn't have any elements yet: %v", elem) + } + elem, err = stack.Pop() + switch err { + case containers.ErrEmptyQueue: + // Expected, continue + case containers.ErrInvalidQueue: + t.Fatal("New queue is invalid") + case nil: + t.Fatalf("New queue shouldn't have any elements yet: %v", elem) + } + if !stack.IsEmpty() { + t.Fatal("New queue isn't empty") + } + + if err = stack.Push(other.IntoPointer(10)); err != nil { + t.Fatal("Failed to push value onto new queue") + } + if stack.IsEmpty() { + t.Fatal("Queue is empty after pushing a value") + } + + elem, err = stack.Top() + if err != nil { + t.Fatalf("Failed to get top element from queue after inserting one: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &10") + case *elem == 10: + // Expected, continue + default: + t.Fatalf("Got %v; want &10", *elem) + } + + if err = stack.Push(other.IntoPointer(20)); err != nil { + t.Fatal("Failed to push value onto queue") + } + + elem, err = stack.Top() + if err != nil { + t.Fatalf("Failed to get top element from queue after inserting one: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &20") + case *elem == 20: + // Expected, continue + default: + t.Fatalf("Got %v; want &20", *elem) + } + + if stack.IsEmpty() { + t.Fatal("Top shouldn't pop an element from the queue") + } + + elem, err = stack.Pop() + if err != nil { + t.Fatalf("Expected no error while popping a value: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &20") + case *elem == 20: + // Expected, continue + default: + t.Fatalf("Got %v; want &20", *elem) + } + + if stack.IsEmpty() { + t.Fatal("Queue should still have one element left") + } + + elem, err = stack.Pop() + if err != nil { + t.Fatalf("Expected no error while popping a value: %v", err) + } + switch { + case elem == nil: + t.Fatal("Got nil elem; want &10") + case *elem == 10: + // Expected, continue + default: + t.Fatalf("Got %v; want &10", *elem) + } + + if !stack.IsEmpty() { + t.Fatal("Queue should be empty now") + } +} diff --git a/coverage_badge.png b/coverage_badge.png new file mode 100644 index 0000000..0a69742 Binary files /dev/null and b/coverage_badge.png differ diff --git a/embedFsWrapper/wrapper_test.go b/embedFsWrapper/wrapper_test.go new file mode 100644 index 0000000..aeba3a4 --- /dev/null +++ b/embedFsWrapper/wrapper_test.go @@ -0,0 +1,27 @@ +package embedFsWrapper_test + +import ( + "io" + "testing" + "testing/fstest" + + "git.mstar.dev/mstar/goutils/embedFsWrapper" +) + +func TestFSWrapper(t *testing.T) { + mfs := fstest.MapFS{ + "baz/foo": &fstest.MapFile{Data: []byte("bar")}, + } + wrapped := embedFsWrapper.NewFSWrapper(mfs, "baz/") + f, err := wrapped.Open("foo") + if err != nil { + t.Fatalf("Expected to open file foo (baz/foo), got %v", err) + } + data, err := io.ReadAll(f) + if err != nil { + t.Fatal(err) + } + if string(data) != "bar" { + t.Fatalf("Expected file to have \"bar\" as content, found %v", string(data)) + } +} diff --git a/http/chain_test.go b/http/chain_test.go new file mode 100644 index 0000000..71958a8 --- /dev/null +++ b/http/chain_test.go @@ -0,0 +1,33 @@ +package webutils_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + webutils "git.mstar.dev/mstar/goutils/http" +) + +func TestChainMiddlewares(t *testing.T) { + wrapper1 := webutils.HandlerBuilder(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.Header.Add("X-Foo", "bar") + h.ServeHTTP(w, r) + }) + }) + wrapper2 := webutils.HandlerBuilder(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.Header.Add("X-Foos", r.Header.Get("X-Foo")+"baz") + h.ServeHTTP(w, r) + }) + }) + baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if x := r.Header.Get("X-Foos"); x != "barbaz" { + t.Fatalf(`Expected header "X-Foos" to be "barbaz", got %v`, x) + } + }) + handler := webutils.ChainMiddlewares(baseHandler, wrapper1, wrapper2) + recorder := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + handler.ServeHTTP(recorder, req) +} diff --git a/http/context_test.go b/http/context_test.go new file mode 100644 index 0000000..86026b1 --- /dev/null +++ b/http/context_test.go @@ -0,0 +1,23 @@ +package webutils_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + webutils "git.mstar.dev/mstar/goutils/http" +) + +func TestContextValsMiddleware(t *testing.T) { + builder := webutils.ContextValsMiddleware(map[any]any{"foo": "bar"}) + baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if val, _ := r.Context().Value("foo").(string); val != "bar" { + t.Fatalf(`Expected context value "foo" to be "bar", got %v`, val) + } + }) + wrapped := builder(baseHandler) + recorder := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + wrapped.ServeHTTP(recorder, req) + // At this point baseHandler should have been called and checked for the context value +} diff --git a/http/httpErr_test.go b/http/httpErr_test.go new file mode 100644 index 0000000..75dfce7 --- /dev/null +++ b/http/httpErr_test.go @@ -0,0 +1,235 @@ +package webutils_test + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + webutils "git.mstar.dev/mstar/goutils/http" + "git.mstar.dev/mstar/goutils/maputils" + "git.mstar.dev/mstar/goutils/other" + "git.mstar.dev/mstar/goutils/sliceutils" +) + +func TestHttpErr(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.HttpErr(recorder, 1, "some error", 500) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + res := recorder.Result() + if res.StatusCode != 500 { + t.Fatalf("Expected status code 500, got %v", res.StatusCode) + } + body := string(other.Must(io.ReadAll(res.Body))) + expected := `{"id": 1, "message": "some error"}` + if body != expected { + t.Fatalf("Expected body %q, got %q", expected, body) + } +} + +func TestProblemDetailsStatusOnly(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.ProblemDetailsStatusOnly(recorder, 500) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + res := recorder.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/problem+json" { + t.Fatalf("Expected content type to be \"application/problem+json\" got %v", ct) + } + body := string(other.Must(io.ReadAll(res.Body))) + data := map[string]any{} + err = json.Unmarshal([]byte(body), &data) + if err != nil { + t.Fatalf("response body is not json: %v", err) + } + if dt, ok := data["type"].(string); !ok || dt != "about:blank" { + t.Fatalf(`Expected key "type" as "about:blank", got %v`, dt) + } + if dt, ok := data["title"].(string); !ok || dt != http.StatusText(500) { + t.Fatalf(`Expected key "title" as "%v", got %v`, http.StatusText(500), dt) + } + if dt, ok := data["status"].(float64); !ok || dt != 500 { + t.Fatalf(`Expected key "status" as 500, got %v`, dt) + } + if dt, ok := data["reference"].(string); !ok || dt != "RFC 9457" { + t.Fatalf(`Expected key "reference" as "RFC 9457", got %v`, dt) + } +} + +func TestProblemDetailsNoDetailsOrExtras(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.ProblemDetails( + recorder, + 500, + "error-type", + "error-title", + nil, + nil, + ) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + res := recorder.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/problem+json" { + t.Fatalf("Expected content type to be \"application/problem+json\" got %v", ct) + } + body := string(other.Must(io.ReadAll(res.Body))) + data := map[string]any{} + err = json.Unmarshal([]byte(body), &data) + if err != nil { + t.Fatalf("response body is not json: %v", err) + } + + expectedFields := []string{"type", "title", "status"} + gotFields := maputils.KeysFromMap(data) + if !sliceutils.CompareUnordered(gotFields, expectedFields) { + t.Fatalf("Expected fields %v, got %v", expectedFields, gotFields) + } + if dt, ok := data["type"].(string); !ok || dt != "error-type" { + t.Fatalf(`Expected key "type" as "error-type", got %v`, dt) + } + if dt, ok := data["title"].(string); !ok || dt != "error-title" { + t.Fatalf(`Expected key "title" as "error-title", got %v`, dt) + } + if dt, ok := data["status"].(float64); !ok || dt != 500 { + t.Fatalf(`Expected key "status" as 500, got %v`, dt) + } +} + +func TestProblemDetailsNoDetailsWithExtras(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.ProblemDetails( + recorder, + 500, + "error-type", + "error-title", + nil, + map[string]any{"foo": "bar"}, + ) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + res := recorder.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/problem+json" { + t.Fatalf("Expected content type to be \"application/problem+json\" got %v", ct) + } + body := string(other.Must(io.ReadAll(res.Body))) + data := map[string]any{} + err = json.Unmarshal([]byte(body), &data) + if err != nil { + t.Fatalf("response body is not json: %v", err) + } + + expectedFields := []string{"type", "title", "status", "foo"} + gotFields := maputils.KeysFromMap(data) + if !sliceutils.CompareUnordered(gotFields, expectedFields) { + t.Fatalf("Expected fields %v, got %v", expectedFields, gotFields) + } + if dt, ok := data["type"].(string); !ok || dt != "error-type" { + t.Fatalf(`Expected key "type" as "error-type", got %v`, dt) + } + if dt, ok := data["title"].(string); !ok || dt != "error-title" { + t.Fatalf(`Expected key "title" as "error-title", got %v`, dt) + } + if dt, ok := data["status"].(float64); !ok || dt != 500 { + t.Fatalf(`Expected key "status" as 500, got %v`, dt) + } + if dt, ok := data["foo"].(string); !ok || dt != "bar" { + t.Fatalf(`Expected key "foo" as "bar", got %v`, dt) + } +} + +func TestProblemDetailsWithDetailsNoExtras(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.ProblemDetails( + recorder, + 500, + "error-type", + "error-title", + other.IntoPointer("error-details"), + nil, + ) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + res := recorder.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/problem+json" { + t.Fatalf("Expected content type to be \"application/problem+json\" got %v", ct) + } + body := string(other.Must(io.ReadAll(res.Body))) + data := map[string]any{} + err = json.Unmarshal([]byte(body), &data) + if err != nil { + t.Fatalf("response body is not json: %v", err) + } + + expectedFields := []string{"type", "title", "status", "detail"} + gotFields := maputils.KeysFromMap(data) + if !sliceutils.CompareUnordered(gotFields, expectedFields) { + t.Fatalf("Expected fields %v, got %v", expectedFields, gotFields) + } + if dt, ok := data["type"].(string); !ok || dt != "error-type" { + t.Fatalf(`Expected key "type" as "error-type", got %v`, dt) + } + if dt, ok := data["title"].(string); !ok || dt != "error-title" { + t.Fatalf(`Expected key "title" as "error-title", got %v`, dt) + } + if dt, ok := data["status"].(float64); !ok || dt != 500 { + t.Fatalf(`Expected key "status" as 500, got %v`, dt) + } + if dt, ok := data["detail"].(string); !ok || dt != "error-details" { + t.Fatalf(`Expected key "detail" as "error-details", got %v`, dt) + } +} + +func TestProblemDetailsWithDetailsWithExtras(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.ProblemDetails( + recorder, + 500, + "error-type", + "error-title", + other.IntoPointer("error-details"), + map[string]any{"foo": "bar", "type": "should not be set in the end"}, + ) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + res := recorder.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/problem+json" { + t.Fatalf("Expected content type to be \"application/problem+json\" got %v", ct) + } + body := string(other.Must(io.ReadAll(res.Body))) + data := map[string]any{} + err = json.Unmarshal([]byte(body), &data) + if err != nil { + t.Fatalf("response body is not json: %v", err) + } + + expectedFields := []string{"type", "title", "status", "detail", "foo"} + gotFields := maputils.KeysFromMap(data) + if !sliceutils.CompareUnordered(gotFields, expectedFields) { + t.Fatalf("Expected fields %v, got %v", expectedFields, gotFields) + } + if dt, ok := data["type"].(string); !ok || dt != "error-type" { + t.Fatalf(`Expected key "type" as "error-type", got %v`, dt) + } + if dt, ok := data["title"].(string); !ok || dt != "error-title" { + t.Fatalf(`Expected key "title" as "error-title", got %v`, dt) + } + if dt, ok := data["status"].(float64); !ok || dt != 500 { + t.Fatalf(`Expected key "status" as 500, got %v`, dt) + } + if dt, ok := data["detail"].(string); !ok || dt != "error-details" { + t.Fatalf(`Expected key "detail" as "error-details", got %v`, dt) + } + if dt, ok := data["foo"].(string); !ok || dt != "bar" { + t.Fatalf(`Expected key "foo" as "bar", got %v`, dt) + } +} diff --git a/http/json_test.go b/http/json_test.go new file mode 100644 index 0000000..23236f8 --- /dev/null +++ b/http/json_test.go @@ -0,0 +1,45 @@ +package webutils_test + +import ( + "io" + "net/http/httptest" + "testing" + + webutils "git.mstar.dev/mstar/goutils/http" +) + +func TestSendJsonOk(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.SendJson(recorder, map[string]any{ + "a": 1, + "b": "b", + "c": nil, + }) + if err != nil { + t.Fatalf("error while sending json: %v", err) + } + res := recorder.Result() + if ctype := res.Header.Get("Content-Type"); ctype != "application/json" { + t.Fatalf("expected content type \"application/json\", got %v", ctype) + } + // httptest guarantees body to be functional and only returned error will be EOL + // so no need to watch for error + bodyBytes, _ := io.ReadAll(res.Body) + body := string(bodyBytes) + expected := `{"a":1,"b":"b","c":null}` + if body != expected { + t.Fatalf("Expected %v as body, got %v", expected, body) + } +} + +func TestSendJsonNotMarshallable(t *testing.T) { + recorder := httptest.NewRecorder() + err := webutils.SendJson(recorder, map[string]any{ + "a": 1, + "b": "b", + "c": make(chan any), + }) + if err == nil { + t.Fatal("expected json marshalling error") + } +} diff --git a/maputils/mapUtils_test.go b/maputils/mapUtils_test.go new file mode 100644 index 0000000..61191f0 --- /dev/null +++ b/maputils/mapUtils_test.go @@ -0,0 +1,274 @@ +package maputils_test + +import ( + "reflect" + "testing" + + "git.mstar.dev/mstar/goutils/maputils" + "git.mstar.dev/mstar/goutils/sliceutils" +) + +func TestMapSameKeys(t *testing.T) { + type args struct { + dic map[string]int + apply func(string, int) int + } + tests := []struct { + name string + args args + want map[string]int + }{ + { + name: "map", + args: args{ + dic: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + apply: func(s string, i int) int { return i + 1 }, + }, + want: map[string]int{ + "a": 2, + "b": 3, + "c": 4, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maputils.MapSameKeys(tt.args.dic, tt.args.apply); !reflect.DeepEqual( + got, + tt.want, + ) { + t.Errorf("MapSameKeys() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapNewKeys(t *testing.T) { + type args struct { + in map[string]int + apply func(string, int) (string, int) + } + tests := []struct { + name string + args args + want map[string]int + }{ + { + name: "new keys", + args: args{ + in: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + apply: func(s string, i int) (string, int) { return s + "-", i + 1 }, + }, + want: map[string]int{ + "a-": 2, + "b-": 3, + "c-": 4, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maputils.MapNewKeys(tt.args.in, tt.args.apply); !reflect.DeepEqual( + got, + tt.want, + ) { + t.Errorf("MapNewKeys() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFilterMap(t *testing.T) { + type args struct { + dic map[string]int + filter func(string, int) bool + } + tests := []struct { + name string + args args + want map[string]int + }{ + { + name: "filter", + args: args{ + dic: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + filter: func(s string, i int) bool { return s == "a" }, + }, + want: map[string]int{"a": 1}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maputils.FilterMap(tt.args.dic, tt.args.filter); !reflect.DeepEqual( + got, + tt.want, + ) { + t.Errorf("FilterMap() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestKeysFromMap(t *testing.T) { + type args struct { + m map[string]int + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "keys", + args: args{ + m: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + }, + want: []string{"a", "b", "c"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maputils.KeysFromMap(tt.args.m); !sliceutils.CompareUnordered(got, tt.want) { + t.Errorf("KeysFromMap() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompareMap(t *testing.T) { + type args struct { + a map[string]int + b map[string]int + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "equal", + args: args{ + a: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + b: map[string]int{ + "b": 2, + "a": 1, + "c": 3, + }, + }, + want: true, + }, + { + name: "not equal, same len, same keys", + args: args{ + a: map[string]int{ + "a": 5, + "b": 6, + "c": 7, + }, + b: map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, + }, + want: false, + }, + { + name: "not equal, diff len", + args: args{ + a: map[string]int{ + "a": 5, + "b": 6, + "c": 7, + }, + b: map[string]int{ + "a": 1, + "b": 2, + }, + }, + want: false, + }, + { + name: "not equal, same len, diff keys", + args: args{ + a: map[string]int{ + "a": 5, + "b": 6, + "c": 7, + }, + b: map[string]int{ + "e": 1, + "f": 2, + "g": 3, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maputils.CompareMap(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("CompareMap() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompact(t *testing.T) { + type args struct { + m map[int]int + compactor func(accK int, accV int, nextK int, nextV int) (int, int) + } + tests := []struct { + name string + args args + want int + want1 int + }{ + // TODO: Add test cases. + { + name: "compact", + args: args{ + m: map[int]int{ + 4: 1, + 5: 2, + 6: 3, + }, + compactor: func(accK int, accV int, nextK int, nextV int) (int, int) { return accK + nextK, accV + nextV }, + }, + want: 4 + 5 + 6, + want1: 1 + 2 + 3, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := maputils.Compact(tt.args.m, tt.args.compactor) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Compact() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("Compact() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} diff --git a/math/math_test.go b/math/math_test.go new file mode 100644 index 0000000..effee83 --- /dev/null +++ b/math/math_test.go @@ -0,0 +1,16 @@ +package mathutils_test + +import ( + "testing" + + mathutils "git.mstar.dev/mstar/goutils/math" +) + +func TestAbs(t *testing.T) { + if x := mathutils.Abs(-5); x != 5 { + t.Fatalf("Received %v, wanted 5", x) + } + if x := mathutils.Abs(6); x != 6 { + t.Fatalf("Received %v, wanted 6", x) + } +} diff --git a/other/other_test.go b/other/other_test.go new file mode 100644 index 0000000..f543121 --- /dev/null +++ b/other/other_test.go @@ -0,0 +1,55 @@ +package other_test + +import ( + "errors" + "testing" + + "git.mstar.dev/mstar/goutils/other" +) + +func TestOutputIntoChannel(t *testing.T) { + c := make(chan int) + go func() { + other.OutputIntoChannel(10, c) + }() + x := <-c + if x != 10 { + t.Fatalf("sent %v, expected 10", x) + } +} + +func TestMust_mustPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("Must didn't panic on error") + } + }() + _ = other.Must(10, errors.New("some error")) +} + +func TestMust_noPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("Must panicked: %v", r) + } + }() + x := other.Must(10, nil) + if x != 10 { + t.Fatalf("got %v, expected 10", x) + } +} + +func TestIntoPointer(t *testing.T) { + p := other.IntoPointer(10) + if *p != 10 { + t.Fatalf("other.IntoPointer(10) = %v, want &10", p) + } +} + +func TestError(t *testing.T) { + e := errors.New("some error") + we := other.Error("source", "message", e) + if we.Error() != "source: message: some error" { + t.Fatalf("Got %v, expected \"source: message: some error\"", we) + } +} diff --git a/other/zerolog.go b/other/zerolog.go index 12adec1..b2da351 100644 --- a/other/zerolog.go +++ b/other/zerolog.go @@ -1,5 +1,8 @@ package other +// Can't really test zerolog setup functions as they rely on +// CLI flags + import ( "flag" "io" @@ -72,5 +75,4 @@ func configOutputs(logWriter io.Writer) { append([]io.Writer{log.Logger}, extraLogWriters...)..., )).With().Timestamp().Logger() } - return } diff --git a/sliceutils/sliceUtils_test.go b/sliceutils/sliceUtils_test.go new file mode 100644 index 0000000..107b0ae --- /dev/null +++ b/sliceutils/sliceUtils_test.go @@ -0,0 +1,243 @@ +package sliceutils_test + +import ( + "reflect" + "testing" + + "git.mstar.dev/mstar/goutils/sliceutils" +) + +func TestMap(t *testing.T) { + in := []int8{1, 2, 3, 4} + out := []int16{2, 4, 6, 8} + got := sliceutils.Map(in, func(t int8) int16 { return int16(t) * 2 }) + if !reflect.DeepEqual(out, got) { + t.Fatalf("Map() = %v, want %v", got, out) + } +} + +func TestFilter(t *testing.T) { + in := []int8{1, 2, 3, 4} + out := []int8{2, 4} + got := sliceutils.Filter(in, func(t int8) bool { return t%2 == 0 }) + if !reflect.DeepEqual(out, got) { + t.Fatalf("Map() = %v, want %v", got, out) + } +} + +func TestRemoveDuplicate(t *testing.T) { + in := []int8{1, 2, 2, 3, 3, 3, 4} + out := []int8{1, 2, 3, 4} + got := sliceutils.RemoveDuplicate(in) + if !reflect.DeepEqual(out, got) { + t.Fatalf("Map() = %v, want %v", got, out) + } +} + +func TestReverse(t *testing.T) { + in := []int8{1, 2, 3, 4} + out := []int8{4, 3, 2, 1} + sliceutils.Reverse(in) + if !reflect.DeepEqual(out, in) { + t.Fatalf("Map() = %v, want %v", in, out) + } +} + +func TestCompareOrdered(t *testing.T) { + type args struct { + a []int + b []int + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "equal", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{1, 2, 3, 4}, + }, + want: true, + }, + { + name: "not equal, same len", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{0, 1, 2, 3}, + }, + want: false, + }, + { + name: "not equal, diff len", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{0, 1, 2}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sliceutils.CompareOrdered(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("CompareOrdered() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompareUnordered(t *testing.T) { + type args struct { + a []int + b []int + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "equal ordered", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{1, 2, 3, 4}, + }, + want: true, + }, + { + name: "equal unordered", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{2, 4, 3, 1}, + }, + want: true, + }, + { + name: "not equal, same len", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{0, 2, 3, 4}, + }, + want: false, + }, + { + name: "not equal, diff len", + args: args{ + a: []int{1, 2, 3, 4}, + b: []int{0, 1, 2}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sliceutils.CompareUnordered(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("CompareUnordered() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestContains(t *testing.T) { + type args struct { + a []int + b int + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "contains", + args: args{ + a: []int{1, 2, 3, 4}, + b: 3, + }, + want: true, + }, + { + name: "doesnt contain", + args: args{ + a: []int{1, 2, 3, 4}, + b: 0, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sliceutils.Contains(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("Contains() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestContainsFunc(t *testing.T) { + type args struct { + a []int + f func(t int) bool + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "contains", + args: args{ + a: []int{1, 2, 3, 4}, + f: func(t int) bool { return t == 2 }, + }, + want: true, + }, + { + name: "doesnt contain", + args: args{ + a: []int{1, 2, 3, 4}, + f: func(t int) bool { return t == 9 }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sliceutils.ContainsFunc(tt.args.a, tt.args.f); got != tt.want { + t.Errorf("ContainsFunc() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompact(t *testing.T) { + type args struct { + a []int + compactor func(acc int, next int) int + } + tests := []struct { + name string + args args + want int + }{ + { + name: "sum", + args: args{ + a: []int{1, 2, 3, 4}, + compactor: func(acc, next int) int { return acc + next }, + }, + want: 1 + 2 + 3 + 4, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sliceutils.Compact(tt.args.a, tt.args.compactor); !reflect.DeepEqual( + got, + tt.want, + ) { + t.Errorf("Compact() = %v, want %v", got, tt.want) + } + }) + } +}