From 265065a3877633dd8603742472aadf174c400a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Henrique=20Guard=C3=A3o=20Gandarez?= Date: Sat, 9 Nov 2024 08:59:17 -0300 Subject: [PATCH] Add missing tests for cmd/offline --- cmd/heartbeat/heartbeat_test.go | 12 +- cmd/offline/offline_test.go | 113 +++++++++++++++ cmd/offline/testdata/extra_heartbeats.json | 1 + cmd/offline/testdata/localfile.go | 3 + cmd/offline/testdata/main.go | 11 ++ cmd/offline/testdata/main.py | 20 +++ cmd/run_internal_test.go | 160 +++++++++++++++++++++ 7 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 cmd/offline/offline_test.go create mode 100644 cmd/offline/testdata/extra_heartbeats.json create mode 100644 cmd/offline/testdata/localfile.go create mode 100644 cmd/offline/testdata/main.go create mode 100644 cmd/offline/testdata/main.py diff --git a/cmd/heartbeat/heartbeat_test.go b/cmd/heartbeat/heartbeat_test.go index 47f65e04..2b40ff92 100644 --- a/cmd/heartbeat/heartbeat_test.go +++ b/cmd/heartbeat/heartbeat_test.go @@ -52,6 +52,8 @@ func TestSendHeartbeats(t *testing.T) { tmpFile, err := os.CreateTemp(t.TempDir(), "wakatime-config") require.NoError(t, err) + defer tmpFile.Close() + subfolders := project.CountSlashesInProjectFolder(projectFolder) router.HandleFunc("/users/current/heartbeats.bulk", func(w http.ResponseWriter, req *http.Request) { @@ -544,12 +546,12 @@ func TestSendHeartbeats_NonExistingEntity(t *testing.T) { ctx = log.ToContext(ctx, logger) - f, err := os.CreateTemp(tmpDir, "") + offlineQueueFile, err := os.CreateTemp(tmpDir, "") require.NoError(t, err) - defer f.Close() + defer offlineQueueFile.Close() - err = cmdheartbeat.SendHeartbeats(ctx, v, f.Name()) + err = cmdheartbeat.SendHeartbeats(ctx, v, offlineQueueFile.Name()) require.NoError(t, err) output, err := io.ReadAll(logFile) @@ -1061,6 +1063,8 @@ func TestSendHeartbeats_ObfuscateProject(t *testing.T) { offlineQueueFile, err := os.CreateTemp(t.TempDir(), "") require.NoError(t, err) + defer offlineQueueFile.Close() + err = cmdheartbeat.SendHeartbeats(ctx, v, offlineQueueFile.Name()) require.NoError(t, err) @@ -1150,6 +1154,8 @@ func TestSendHeartbeats_ObfuscateProjectNotBranch(t *testing.T) { offlineQueueFile, err := os.CreateTemp(t.TempDir(), "") require.NoError(t, err) + defer offlineQueueFile.Close() + err = cmdheartbeat.SendHeartbeats(ctx, v, offlineQueueFile.Name()) require.NoError(t, err) diff --git a/cmd/offline/offline_test.go b/cmd/offline/offline_test.go new file mode 100644 index 00000000..b308760a --- /dev/null +++ b/cmd/offline/offline_test.go @@ -0,0 +1,113 @@ +package offline_test + +import ( + "context" + "encoding/json" + "os" + "testing" + + cmdoffline "github.com/wakatime/wakatime-cli/cmd/offline" + "github.com/wakatime/wakatime-cli/pkg/heartbeat" + "github.com/wakatime/wakatime-cli/pkg/offline" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSaveHeartbeats(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer tmpFile.Close() + + offlineQueueFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer offlineQueueFile.Close() + + ctx := context.Background() + + v := viper.New() + v.Set("config", tmpFile.Name()) + v.Set("category", "debugging") + v.Set("cursorpos", 42) + v.Set("entity", "testdata/main.go") + v.Set("entity-type", "file") + v.Set("key", "00000000-0000-4000-8000-000000000000") + v.Set("language", "Go") + v.Set("alternate-language", "Golang") + v.Set("hide-branch-names", true) + v.Set("project", "wakatime-cli") + v.Set("lineno", 13) + v.Set("time", 1585598059.1) + v.Set("timeout", 5) + v.Set("write", true) + + err = cmdoffline.SaveHeartbeats(ctx, v, nil, offlineQueueFile.Name()) + require.NoError(t, err) + + offlineCount, err := offline.CountHeartbeats(ctx, offlineQueueFile.Name()) + require.NoError(t, err) + + assert.Equal(t, 1, offlineCount) +} + +func TestSaveHeartbeats_ExtraHeartbeats(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer tmpFile.Close() + + offlineQueueFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer offlineQueueFile.Close() + + ctx := context.Background() + + data, err := os.ReadFile("testdata/extra_heartbeats.json") + require.NoError(t, err) + + var hh []heartbeat.Heartbeat + + err = json.Unmarshal(data, &hh) + require.NoError(t, err) + + v := viper.New() + v.Set("config", tmpFile.Name()) + v.Set("entity", "testdata/main.go") + v.Set("key", "00000000-0000-4000-8000-000000000000") + + err = cmdoffline.SaveHeartbeats(ctx, v, hh, offlineQueueFile.Name()) + require.NoError(t, err) + + offlineCount, err := offline.CountHeartbeats(ctx, offlineQueueFile.Name()) + require.NoError(t, err) + + assert.Equal(t, 25, offlineCount) +} + +func TestSaveHeartbeats_OfflineDisabled(t *testing.T) { + tmpFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer tmpFile.Close() + + offlineQueueFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer offlineQueueFile.Close() + + ctx := context.Background() + + v := viper.New() + v.Set("config", tmpFile.Name()) + v.Set("disable-offline", true) + v.Set("entity", "testdata/main.go") + v.Set("key", "00000000-0000-4000-8000-000000000000") + + err = cmdoffline.SaveHeartbeats(ctx, v, nil, offlineQueueFile.Name()) + + assert.EqualError(t, err, "saving to offline db disabled") +} diff --git a/cmd/offline/testdata/extra_heartbeats.json b/cmd/offline/testdata/extra_heartbeats.json new file mode 100644 index 00000000..6e043da1 --- /dev/null +++ b/cmd/offline/testdata/extra_heartbeats.json @@ -0,0 +1 @@ +[ { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598059 }, { "alternate_language": "Py", "category": "debugging", "cursorpos": null, "entity": "testdata/main.py", "is_write": null, "language": "Python", "lineno": null, "lines": null, "project": "wakatime-cli", "type": "file", "timestamp": 1585598060 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598061 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598062 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598063 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598064 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598065 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598066 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598067 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598068 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598069 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598070 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598071 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598072 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598073 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598074 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598075 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598076 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598077 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598078 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598079 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598080 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598081 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598082 }, { "alternate_language": "Golang", "alternate_project": "billing", "category": "coding", "cursorpos": 12, "entity": "testdata/main.go", "entity_type": "file", "is_write": true, "language": "Go", "lineno": 42, "lines": 45, "project": "wakatime-cli", "time": 1585598083 } ] \ No newline at end of file diff --git a/cmd/offline/testdata/localfile.go b/cmd/offline/testdata/localfile.go new file mode 100644 index 00000000..5626cbe7 --- /dev/null +++ b/cmd/offline/testdata/localfile.go @@ -0,0 +1,3 @@ +hello +hello +world diff --git a/cmd/offline/testdata/main.go b/cmd/offline/testdata/main.go new file mode 100644 index 00000000..c6e84062 --- /dev/null +++ b/cmd/offline/testdata/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + fmt.Println("hello world") + os.Exit(0) +} diff --git a/cmd/offline/testdata/main.py b/cmd/offline/testdata/main.py new file mode 100644 index 00000000..56ff0925 --- /dev/null +++ b/cmd/offline/testdata/main.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# vim: set filetype=python + +from __future__ import print_statement + +import os, sys +from flask import session, render_template +import simplejson as json + + +class MyClass(object): + """this class + """ + + def method1(self): + a = 1 + 2 + b = 'hello world!' + for x in y: + print(x) + raise Exception() diff --git a/cmd/run_internal_test.go b/cmd/run_internal_test.go index 28885ef0..728ae750 100644 --- a/cmd/run_internal_test.go +++ b/cmd/run_internal_test.go @@ -46,6 +46,166 @@ func TestRunCmd_Err(t *testing.T) { assert.Equal(t, exitcode.ErrGeneric, err.(exitcode.Err).Code) } +func TestRunCmd_Panic(t *testing.T) { + testServerURL, router, tearDown := setupTestServer() + defer tearDown() + + version.OS = "some os" + version.Arch = "some architecture" + version.Version = "some version" + + router.HandleFunc("/plugins/errors", func(w http.ResponseWriter, req *http.Request) { + // check request + assert.Equal(t, http.MethodPost, req.Method) + assert.Equal(t, []string{"Basic MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw"}, req.Header["Authorization"]) + assert.Equal(t, []string{"application/json"}, req.Header["Content-Type"]) + + expectedBodyTpl, err := os.ReadFile("testdata/diagnostics_request_template.json") + require.NoError(t, err) + + body, err := io.ReadAll(req.Body) + require.NoError(t, err) + + var diagnostics struct { + Architecture string `json:"architecture"` + CliVersion string `json:"cli_version"` + Editor string `json:"editor"` + Logs string `json:"logs"` + OriginalError string `json:"error_message"` + Platform string `json:"platform"` + Plugin string `json:"plugin"` + Stack string `json:"stacktrace"` + } + + err = json.Unmarshal(body, &diagnostics) + require.NoError(t, err) + + expectedBodyStr := fmt.Sprintf( + string(expectedBodyTpl), + jsonEscape(t, diagnostics.OriginalError), + jsonEscape(t, diagnostics.Logs), + jsonEscape(t, diagnostics.Stack), + ) + + assert.JSONEq(t, expectedBodyStr, string(body)) + + // send response + w.WriteHeader(http.StatusCreated) + }) + + logFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer logFile.Close() + + ctx := context.Background() + + v := viper.New() + v.Set("api-url", testServerURL) + v.Set("log-file", logFile.Name()) + + logger, err := SetupLogging(ctx, v) + require.NoError(t, err) + + defer logger.Flush() + + ctx = log.ToContext(ctx, logger) + + err = runCmd(ctx, v, false, false, func(_ context.Context, _ *viper.Viper) (int, error) { + panic("fail") + }) + + var errexitcode exitcode.Err + + require.ErrorAs(t, err, &errexitcode) + assert.Equal(t, exitcode.ErrGeneric, err.(exitcode.Err).Code) + + output, err := io.ReadAll(logFile) + require.NoError(t, err) + + assert.Contains(t, string(output), "panicked") +} + +func TestRunCmd_Panic_Verbose(t *testing.T) { + testServerURL, router, tearDown := setupTestServer() + defer tearDown() + + version.OS = "some os" + version.Arch = "some architecture" + version.Version = "some version" + + router.HandleFunc("/plugins/errors", func(w http.ResponseWriter, req *http.Request) { + // check request + assert.Equal(t, http.MethodPost, req.Method) + assert.Equal(t, []string{"Basic MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw"}, req.Header["Authorization"]) + assert.Equal(t, []string{"application/json"}, req.Header["Content-Type"]) + + expectedBodyTpl, err := os.ReadFile("testdata/diagnostics_request_template.json") + require.NoError(t, err) + + body, err := io.ReadAll(req.Body) + require.NoError(t, err) + + var diagnostics struct { + Architecture string `json:"architecture"` + CliVersion string `json:"cli_version"` + Editor string `json:"editor"` + Logs string `json:"logs"` + OriginalError string `json:"error_message"` + Platform string `json:"platform"` + Plugin string `json:"plugin"` + Stack string `json:"stacktrace"` + } + + err = json.Unmarshal(body, &diagnostics) + require.NoError(t, err) + + expectedBodyStr := fmt.Sprintf( + string(expectedBodyTpl), + jsonEscape(t, diagnostics.OriginalError), + jsonEscape(t, diagnostics.Logs), + jsonEscape(t, diagnostics.Stack), + ) + + assert.JSONEq(t, expectedBodyStr, string(body)) + + // send response + w.WriteHeader(http.StatusCreated) + }) + + logFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + + defer logFile.Close() + + ctx := context.Background() + + v := viper.New() + v.Set("api-url", testServerURL) + v.Set("log-file", logFile.Name()) + + logger, err := SetupLogging(ctx, v) + require.NoError(t, err) + + defer logger.Flush() + + ctx = log.ToContext(ctx, logger) + + err = runCmd(ctx, v, true, false, func(_ context.Context, _ *viper.Viper) (int, error) { + panic("fail") + }) + + var errexitcode exitcode.Err + + require.ErrorAs(t, err, &errexitcode) + assert.Equal(t, exitcode.ErrGeneric, err.(exitcode.Err).Code) + + output, err := io.ReadAll(logFile) + require.NoError(t, err) + + assert.Contains(t, string(output), "panicked") +} + func TestRunCmd_ErrOfflineEnqueue(t *testing.T) { testServerURL, router, tearDown := setupTestServer() defer tearDown()