diff options
author | EuAndreh <eu@euandre.org> | 2024-08-08 10:39:32 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-08-08 11:23:19 -0300 |
commit | 340608528f9bf41118e4822726092f40f8253c7b (patch) | |
tree | 3456c7e7b42ff16a129bdf498ddf04545880f4a6 | |
parent | tests/gobang.go: Add tests to more functions (diff) | |
download | gobang-340608528f9bf41118e4822726092f40f8253c7b.tar.gz gobang-340608528f9bf41118e4822726092f40f8253c7b.tar.xz |
tests/gobang.go: Add tests for most missing functions
-rw-r--r-- | src/gobang.go | 143 | ||||
-rw-r--r-- | tests/gobang.go | 614 |
2 files changed, 674 insertions, 83 deletions
diff --git a/src/gobang.go b/src/gobang.go index fff0503..b1fe281 100644 --- a/src/gobang.go +++ b/src/gobang.go @@ -15,7 +15,9 @@ import ( "math/bits" "os" "reflect" + "runtime" "runtime/debug" + "slices" "strings" "sync" "syscall" @@ -410,9 +412,6 @@ func Hash(password []byte, salt []byte) []byte{ return hash } -// FIXME: finish rewriting -// FIXME: add tests -// // getV7Time returns the time in milliseconds and nanoseconds / 256. // The returned (milli << (12 + seq)) is guaranteed to be greater than // (milli << (12 + seq)) returned by any previous call to getV7Time. @@ -518,6 +517,45 @@ func ParseUUID(str string) (UUID, error) { return UUID { bytes: [uuidByteCount]byte(data) }, nil } +func sourceInfo(skip int) slog.Attr { + pc := make([]uintptr, 10) + n := runtime.Callers(skip, pc) + if n == 0 { + return slog.Group( + "src", + "file", "UNAVAILABLE", + "function", "UNAVAILABLE", + "line", "UNAVAILABLE", + ) + } + + pc = pc[:n] + frames := runtime.CallersFrames(pc) + frame, _ := frames.Next() + return slog.Group( + "src", + "file", frame.File, + "function", frame.Function, + "line", frame.Line, + ) +} + +func logArgs(type_ string) []string { + return []string { + "id", NewUUID().String(), + "kind", "log", + "type", type_, + } +} + +func anyArr[S ~[]E, E any](arr S) []any { + ret := make([]any, len(arr)) + for i , el := range arr { + ret[i] = el + } + return ret +} + func Debug(message string, type_ string, args ...any) { if level < LevelDebug { return @@ -525,13 +563,10 @@ func Debug(message string, type_ string, args ...any) { slog.Debug( message, - append( - []any { - "id", NewUUID(), - "kind", "log", - "type", type_, - }, - args..., + slices.Concat( + anyArr(logArgs(type_)), + []any { sourceInfo(3) }, + args, )..., ) } @@ -543,13 +578,10 @@ func Info(message string, type_ string, args ...any) { slog.Info( message, - append( - []any { - "id", NewUUID(), - "kind", "log", - "type", type_, - }, - args..., + slices.Concat( + anyArr(logArgs(type_)), + []any { sourceInfo(3) }, + args, )..., ) } @@ -561,13 +593,10 @@ func Warning(message string, type_ string, args ...any) { slog.Warn( message, - append( - []any { - "id", NewUUID(), - "kind", "log", - "type", type_, - }, - args..., + slices.Concat( + anyArr(logArgs(type_)), + []any { sourceInfo(3) }, + args, )..., ) } @@ -579,36 +608,46 @@ func Error(message string, type_ string, args ...any) { slog.Error( message, - append( - []any { - "id", NewUUID(), - "kind", "log", - "type", type_, - }, - args..., + slices.Concat( + anyArr(logArgs(type_)), + []any { sourceInfo(3) }, + args, )..., ) } -func Metric(type_ string, label string, args ...any) { +func metric(type_ string, label string, args ...any) { if !emitMetric { return } slog.Info( "_", - append( + slices.Concat( []any { - "id", NewUUID(), + "id", NewUUID().String(), "kind", "metric", "type", type_, "label", label, }, - args..., + []any { sourceInfo(3) }, + args, )..., ) } +func MakeCounter(label string) func(...any) { + return func(args ...any) { + metric( + "counter", label, + slices.Concat( + []any { "value", 1 }, + args, + )..., + ) + } +} + func MakeGauge(label string, staticArgs ...any) Gauge { var zero = big.NewInt(0) var one = big.NewInt(1) @@ -618,25 +657,20 @@ func MakeGauge(label string, staticArgs ...any) Gauge { Error( "Gauge went negative", "process-metric", - append( + slices.Concat( []any { "value", count }, - append( - staticArgs, - dynamicArgs..., - )..., + staticArgs, + dynamicArgs, )..., ) return // avoid wrong metrics being emitted } - Metric( + metric( "gauge", label, - // TODO: we'll have slices.Concat on Go 1.22 - append( + slices.Concat( []any { "value", count }, - append( - staticArgs, - dynamicArgs..., - )..., + staticArgs, + dynamicArgs, )..., ) } @@ -652,15 +686,6 @@ func MakeGauge(label string, staticArgs ...any) Gauge { } } -func MakeCounter(label string) func(...any) { - return func(args ...any) { - Metric( - "counter", label, - append([]any { "value", 1 }, args...)..., - ) - } -} - func ErrorIf(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Unexpected error: %#v\n", err) @@ -727,15 +752,17 @@ func AssertEqualI(i int, given any, expected any) { } } +var unfilteringLevel = new(slog.LevelVar) func setLoggerOutput(w io.Writer) { + unfilteringLevel.Set(slog.LevelDebug) slog.SetDefault(slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions { - AddSource: true, + Level: unfilteringLevel, })).With( slog.Group( "info", "pid", os.Getpid(), "ppid", os.Getppid(), - "puuid", NewUUID(), + "puuid", NewUUID().String(), ), )) } diff --git a/tests/gobang.go b/tests/gobang.go index a03e8bc..842b95a 100644 --- a/tests/gobang.go +++ b/tests/gobang.go @@ -1,16 +1,17 @@ package gobang import ( - "bytes" "crypto/sha1" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" + "errors" "fmt" "hash" "log/slog" "os" + "strings" "time" ) @@ -382,6 +383,10 @@ func test_Hash() { }) } +func test_getV7Time() { + // FIXME +} + func test_newUUIDFrom() { TestStart("newUUIDFrom()") @@ -489,6 +494,458 @@ func test_ParseUUID() { }) } +func test_sourceInfo() { + TestStart("sourceInfo()") + + Testing("sourceInfo() is defined at...", func() { + const FILENAME = "src/gobang.go" + info := sourceInfo(1) + AssertEqual(info.Key, "src") + AssertEqual(len(info.Value.Group()), 3) + + AssertEqual(info.Value.Group()[0].Key, "file") + file := info.Value.Group()[0].Value.String() + AssertEqual(file[len(file) - len(FILENAME):], FILENAME) + + AssertEqual(info.Value.Group()[1].Key, "function") + AssertEqual( + info.Value.Group()[1].Value.String(), + "gobang.sourceInfo", + ) + + AssertEqual(info.Value.Group()[2].Key, "line") + AssertEqual(info.Value.Group()[2].Value.Kind(), slog.KindInt64) + }) + + Testing("we're calling it from...", func() { + const FILENAME = "tests/gobang.go" + info := sourceInfo(2) + AssertEqual(info.Key, "src") + AssertEqual(len(info.Value.Group()), 3) + + AssertEqual(info.Value.Group()[0].Key, "file") + file := info.Value.Group()[0].Value.String() + AssertEqual(file[len(file) - len(FILENAME):], FILENAME) + + AssertEqual(info.Value.Group()[1].Key, "function") + AssertEqual( + info.Value.Group()[1].Value.String(), + "gobang.test_sourceInfo.func2", + ) + + AssertEqual(info.Value.Group()[2].Key, "line") + AssertEqual(info.Value.Group()[2].Value.Kind(), slog.KindInt64) + }) +} + +func test_logArgs() { + TestStart("logArgs()") + + Testing("direct string array return", func() { + args := logArgs("the-type") + AssertEqual(len(args), 6) + AssertEqual(args[0], "id") + + _, err := ParseUUID(args[1]) + ErrorIf(err) + + expected := []string { "kind", "log", "type", "the-type" } + AssertEqual(args[2:], expected) + }) +} + +func test_anyArr() { + TestStart("anyArr()") + + Testing("we can turn []string into []any", func() { + var input []string = []string { "one", "two", "three" } + var given []any = anyArr(input) + var expected []any = []any { "one", "two", "three" } + + AssertEqual(given, expected) + }) +} + +func testLog( + levelName string, + filterLevel logLevel, + fn func(string, string, ...any), +) { + savedLogger := slog.Default() + savedLevel := level + + s := new(strings.Builder) + setLoggerOutput(s) + level = filterLevel + + fn("a-message", "a-type", "a-key", "a-value") + + { + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + timeRaw, ok := data["time"] + AssertEqual(ok, true) + timeStr, ok := timeRaw.(string) + AssertEqual(ok, true) + _, err = time.Parse(time.RFC3339, timeStr) + ErrorIf(err) + + levelRaw, ok := data["level"] + AssertEqual(ok, true) + level, ok := levelRaw.(string) + AssertEqual(ok, true) + AssertEqual(level, levelName) + + kindRaw, ok := data["kind"] + AssertEqual(ok, true) + kind, ok := kindRaw.(string) + AssertEqual(ok, true) + AssertEqual(kind, "log") + + msgRaw, ok := data["msg"] + AssertEqual(ok, true) + msg, ok := msgRaw.(string) + AssertEqual(ok, true) + AssertEqual(msg, "a-message") + + typeRaw, ok := data["type"] + AssertEqual(ok, true) + type_, ok := typeRaw.(string) + AssertEqual(ok, true) + AssertEqual(type_, "a-type") + + keyRaw, ok := data["a-key"] + AssertEqual(ok, true) + key, ok := keyRaw.(string) + AssertEqual(ok, true) + AssertEqual(key, "a-value") + } + + slog.SetDefault(savedLogger) + level = savedLevel +} + +func testNoLog(filterLevel logLevel, fn func(string, string, ...any)) { + savedLogger := slog.Default() + savedLevel := level + + s := new(strings.Builder) + setLoggerOutput(s) + level = filterLevel + fn("a-message", "a-type", "a-key", "a-value") + + AssertEqual(s.String(), "") + + slog.SetDefault(savedLogger) + level = savedLevel +} + +func test_Debug() { + TestStart("Debug()") + + Testing("exists in LevelDebug", func() { + testLog("DEBUG", LevelDebug, Debug) + }) + + Testing("doesn't in lower levels", func() { + testNoLog(LevelInfo, Debug) + }) +} + +func test_Info() { + TestStart("Info()") + + Testing("exists in LevelInfo", func() { + testLog("INFO", LevelInfo, Info) + }) + + Testing("doesn't in lower levels", func() { + testNoLog(LevelWarning, Info) + }) +} + +func test_Warning() { + TestStart("Warning()") + + Testing("exists in LevelWarning", func() { + testLog("WARN" /* WARNING */, LevelWarning, Warning) + }) + + Testing("doesn't in lower levels", func() { + testNoLog(LevelError, Warning) + }) +} + +func test_Error() { + TestStart("Error()") + + Testing("exists in LevelError", func() { + testLog("ERROR", LevelError, Error) + }) + + Testing("doesn't in lower levels", func() { + testNoLog(LevelNone, Error) + }) +} + +func test_metric() { + TestStart("metric()") + + Testing("noop when emitMetric is false", func() { + savedLogger := slog.Default() + savedFlag := emitMetric + + s := new(strings.Builder) + setLoggerOutput(s) + emitMetric = false + + metric("", "") + AssertEqual(s.String(), "") + + slog.SetDefault(savedLogger) + emitMetric = savedFlag + }) + + Testing("JSON entry of kind \"metric\"", func() { + savedLogger := slog.Default() + savedFlag := emitMetric + + s := new(strings.Builder) + setLoggerOutput(s) + emitMetric = true + + metric("a-type", "a-label", "count-something", 123) + + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + timeRaw, ok := data["time"] + AssertEqual(ok, true) + timeStr, ok := timeRaw.(string) + AssertEqual(ok, true) + _, err = time.Parse(time.RFC3339, timeStr) + ErrorIf(err) + + levelRaw, ok := data["level"] + AssertEqual(ok, true) + level, ok := levelRaw.(string) + AssertEqual(ok, true) + AssertEqual(level, "INFO") + + msgRaw, ok := data["msg"] + AssertEqual(ok, true) + msg, ok := msgRaw.(string) + AssertEqual(ok, true) + AssertEqual(msg, "_") + + idRaw, ok := data["id"] + AssertEqual(ok, true) + id, ok := idRaw.(string) + AssertEqual(ok, true) + _, err = ParseUUID(id) + ErrorIf(err) + + kindRaw, ok := data["kind"] + AssertEqual(ok, true) + kind, ok := kindRaw.(string) + AssertEqual(ok, true) + AssertEqual(kind, "metric") + + typeRaw, ok := data["type"] + AssertEqual(ok, true) + type_, ok := typeRaw.(string) + AssertEqual(ok, true) + AssertEqual(type_, "a-type") + + labelRaw, ok := data["label"] + AssertEqual(ok, true) + label, ok := labelRaw.(string) + AssertEqual(ok, true) + AssertEqual(label, "a-label") + + keyRaw, ok := data["count-something"] + AssertEqual(ok, true) + key, ok := keyRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(key), 123) + + slog.SetDefault(savedLogger) + emitMetric = savedFlag + }) +} + +func test_MakeCounter() { + TestStart("MakeCounter()") + + Testing("\"value\" is always 1", func() { + savedLogger := slog.Default() + savedFlag := emitMetric + + s := new(strings.Builder) + setLoggerOutput(s) + emitMetric = true + + emitTCPError := MakeCounter("emit-tcp-error") + emitTCPError("more-data", 555) + + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + typeRaw, ok := data["type"] + AssertEqual(ok, true) + type_, ok := typeRaw.(string) + AssertEqual(ok, true) + AssertEqual(type_, "counter") + + labelRaw, ok := data["label"] + AssertEqual(ok, true) + label, ok := labelRaw.(string) + AssertEqual(ok, true) + AssertEqual(label, "emit-tcp-error") + + valueRaw, ok := data["value"] + AssertEqual(ok, true) + value, ok := valueRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(value), 1) + + keyRaw, ok := data["more-data"] + AssertEqual(ok, true) + key, ok := keyRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(key), 555) + + slog.SetDefault(savedLogger) + emitMetric = savedFlag + }) +} + +func test_MakeGauge() { + TestStart("MakeGauge()") + + Testing("errors on underflow", func() { + savedLogger := slog.Default() + savedLevel := level + + s := new(strings.Builder) + setLoggerOutput(s) + level = LevelError + + activeTCPConnections := MakeGauge("active-tcp-connections") + activeTCPConnections.Dec() + + { + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + levelRaw, ok := data["level"] + AssertEqual(ok, true) + level, ok := levelRaw.(string) + AssertEqual(ok, true) + AssertEqual(level, "ERROR") + + msgRaw, ok := data["msg"] + AssertEqual(ok, true) + msg, ok := msgRaw.(string) + AssertEqual(ok, true) + AssertEqual(msg, "Gauge went negative") + + kindRaw, ok := data["kind"] + AssertEqual(ok, true) + kind, ok := kindRaw.(string) + AssertEqual(ok, true) + AssertEqual(kind, "log") + + typeRaw, ok := data["type"] + AssertEqual(ok, true) + type_, ok := typeRaw.(string) + AssertEqual(ok, true) + AssertEqual(type_, "process-metric") + + valueRaw, ok := data["value"] + AssertEqual(ok, true) + value, ok := valueRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(value), -1) + } + + slog.SetDefault(savedLogger) + level = savedLevel + }) + + Testing("\"value\" grows monotonically", func() { + savedLogger := slog.Default() + savedFlag := emitMetric + + s := new(strings.Builder) + setLoggerOutput(s) + emitMetric = true + + activeTCPConnections := MakeGauge("active-tcp-connections") + activeTCPConnections.Inc() + activeTCPConnections.Inc() + activeTCPConnections.Dec() + activeTCPConnections.Inc() + activeTCPConnections.Inc("more-data", 999) + + strs := strings.Split(s.String(), "\n") + AssertEqual(len(strs), 6) + AssertEqual(strs[5], "") + + var data map[string]interface{} + err := json.Unmarshal([]byte(strs[len(strs) - 2]), &data) + ErrorIf(err) + + typeRaw, ok := data["type"] + AssertEqual(ok, true) + type_, ok := typeRaw.(string) + AssertEqual(ok, true) + AssertEqual(type_, "gauge") + + labelRaw, ok := data["label"] + AssertEqual(ok, true) + label, ok := labelRaw.(string) + AssertEqual(ok, true) + AssertEqual(label, "active-tcp-connections") + + valueRaw, ok := data["value"] + AssertEqual(ok, true) + value, ok := valueRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(value), 3) + + keyRaw, ok := data["more-data"] + AssertEqual(ok, true) + key, ok := keyRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(key), 999) + + slog.SetDefault(savedLogger) + emitMetric = savedFlag + }) +} + +func test_ErrorIf() { + TestStart("ErrorIf()") + + Testing("noop on nil value", func() { + ErrorIf(nil) + }) +} + +func test_ErrorNil() { + TestStart("ErrorNil()") + + Testing("noop on thruthy value", func() { + ErrorNil(errors.New("some error")) + }) +} + func test_showColour() { TestStart("showColour()") @@ -513,6 +970,123 @@ func test_showColour() { ErrorIf(os.Setenv(NAME, savedEnvvar)) } +func test_setLoggerOutput() { + TestStart("setLoggerOutput()") + + savedLogger := slog.Default() + + Testing("the output JSON has data under \"src\"", func() { + s := new(strings.Builder) + setLoggerOutput(s) + Info("", "") + + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + srcRaw, ok := data["src"] + AssertEqual(ok, true) + src, ok := srcRaw.(map[string]interface{}) + AssertEqual(ok, true) + + fileRaw, ok := src["file"] + AssertEqual(ok, true) + file, ok := fileRaw.(string) + AssertEqual(ok, true) + const FILENAME = "tests/gobang.go" + AssertEqual(file[len(file) - len(FILENAME):], FILENAME) + + functionRaw, ok := src["function"] + AssertEqual(ok, true) + function, ok := functionRaw.(string) + AssertEqual(ok, true) + AssertEqual(function, "gobang.test_setLoggerOutput.func1") + + lineRaw, ok := src["line"] + AssertEqual(ok, true) + _, ok = lineRaw.(float64) + AssertEqual(ok, true) + }) + + Testing("the output JSON has data under \"info\"", func() { + s := new(strings.Builder) + setLoggerOutput(s) + Info("", "") + + var data map[string]interface{} + err := json.Unmarshal([]byte(s.String()), &data) + ErrorIf(err) + + infoRaw, ok := data["info"] + AssertEqual(ok, true) + info, ok := infoRaw.(map[string]interface{}) + AssertEqual(ok, true) + + pidRaw, ok := info["pid"] + AssertEqual(ok, true) + pid, ok := pidRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(pid), os.Getpid()) + + ppidRaw, ok := info["ppid"] + AssertEqual(ok, true) + ppid, ok := ppidRaw.(float64) + AssertEqual(ok, true) + AssertEqual(int(ppid), os.Getppid()) + + puuidRaw, ok := info["puuid"] + AssertEqual(ok, true) + puuid, ok := puuidRaw.(string) + AssertEqual(ok, true) + _, err = ParseUUID(puuid) + ErrorIf(err) + }) + + Testing("the puuid is the same across calls to the logger", func() { + s := new(strings.Builder) + setLoggerOutput(s) + Info("", "first") + Info("", "second") + + strs := strings.Split(s.String(), "\n") + AssertEqual(len(strs), 3) + AssertEqual(strs[2], "") + + log1 := strs[0] + log2 := strs[1] + + var puuidFromString = func(str string) string { + var data map[string]interface{} + err := json.Unmarshal([]byte(str), &data) + ErrorIf(err) + + infoRaw, ok := data["info"] + AssertEqual(ok, true) + info, ok := infoRaw.(map[string]interface{}) + AssertEqual(ok, true) + + puuidRaw, ok := info["puuid"] + AssertEqual(ok, true) + puuid, ok := puuidRaw.(string) + AssertEqual(ok, true) + + return puuid + } + + puuid1 := puuidFromString(log1) + puuid2 := puuidFromString(log2) + AssertEqual(puuid1, puuid2) + + uuid1, err := ParseUUID(puuid1) + ErrorIf(err) + uuid2, err := ParseUUID(puuid2) + ErrorIf(err) + AssertEqual(uuid1, uuid2) + }) + + slog.SetDefault(savedLogger) +} + func test_levelFromString() { TestStart("levelFromString()") @@ -640,30 +1214,6 @@ func test_setHostname() { }) } -func TestSetLoggerOutput() { - return - type entry struct { - msg string `json:"msg"` - aKey string `json:"a-key"` - } - var e entry - var buf bytes.Buffer - slog.Error("the message", "a-key", "a-value") - - s := buf.String() - // fmt.Println(s) - // fmt.Println(e) - err := json.Unmarshal([]byte(s), &e) - if err != nil { - // t.Fail() - // Mmain() - } - if e.msg != "the message" { - // t.Fail() - } - fmt.Println(1) - // fmt.Println(e) -} func t1() { test__PBKDF() @@ -680,11 +1230,25 @@ func t3() { func t4() { test_Random() test_Salt() + test_getV7Time() test_newUUIDFrom() test_NewUUID() test_UUIDString() test_ParseUUID() + test_sourceInfo() + test_logArgs() + test_anyArr() + test_Debug() + test_Info() + test_Warning() + test_Error() + test_metric() + test_MakeCounter() + test_MakeGauge() + test_ErrorIf() + test_ErrorNil() test_showColour() + test_setLoggerOutput() test_levelFromString() test_setLogLevel() test_setMetric() |