Skip to content

Commit bdbb2ce

Browse files
committed
Fix integration tests
1 parent da33f99 commit bdbb2ce

16 files changed

+706
-515
lines changed

cmd/fileexperts/fileexperts.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/wakatime/wakatime-cli/cmd/handler"
99
"github.com/wakatime/wakatime-cli/pkg/exitcode"
1010
"github.com/wakatime/wakatime-cli/pkg/fileexperts"
11+
"github.com/wakatime/wakatime-cli/pkg/filter"
1112
"github.com/wakatime/wakatime-cli/pkg/heartbeat"
1213
"github.com/wakatime/wakatime-cli/pkg/log"
1314
"github.com/wakatime/wakatime-cli/pkg/params"
@@ -57,7 +58,7 @@ func FileExperts(ctx context.Context, v *viper.Viper) (string, error) {
5758
return "", fmt.Errorf("failed to initialize api client: %w", err)
5859
}
5960

60-
sender := fileexperts.NewHandle(apiClient)
61+
sender := fileexperts.NewHandle(apiClient, filter.WithLengthValidator())
6162
handle := handler.New(v, handler.Config{
6263
Params: params,
6364
ParamsLoader: LoadParams,
@@ -125,7 +126,6 @@ func initHandleOptions() []handler.Preprocessor {
125126
handler.WithHeartbeatSanitization(),
126127
handler.WithFileExpertsValidation(),
127128
handler.WithRemoteCleanup(),
128-
handler.WithLengthValidator(),
129129
}
130130
}
131131

cmd/handler/option.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,3 @@ func WithRemoteCleanup() Preprocessor {
141141
return remote.WithCleanup()
142142
}
143143
}
144-
145-
// WithLengthValidator returns a Preprocessor that applies length validation to heartbeats.
146-
func WithLengthValidator() Preprocessor {
147-
return func(_ params.Params) heartbeat.HandleOption {
148-
return filter.WithLengthValidator()
149-
}
150-
}

cmd/handler/option_test.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,3 @@ func TestWithRemoteCleanup(t *testing.T) {
167167

168168
assert.Len(t, res, 0)
169169
}
170-
171-
func TestWithLengthValidator(t *testing.T) {
172-
opt := handler.WithLengthValidator()
173-
174-
chain := heartbeat.NewHandle(noopMock{})
175-
hdl := opt(params.Params{})(chain)
176-
177-
res, err := hdl(context.Background(), nil)
178-
require.NoError(t, err)
179-
180-
assert.Len(t, res, 0)
181-
}

cmd/heartbeat/heartbeat.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/wakatime/wakatime-cli/pkg/api"
1313
"github.com/wakatime/wakatime-cli/pkg/backoff"
1414
"github.com/wakatime/wakatime-cli/pkg/exitcode"
15+
"github.com/wakatime/wakatime-cli/pkg/filter"
1516
"github.com/wakatime/wakatime-cli/pkg/heartbeat"
1617
_ "github.com/wakatime/wakatime-cli/pkg/lexer" // force to load all lexers
1718
"github.com/wakatime/wakatime-cli/pkg/log"
@@ -90,10 +91,15 @@ func SendHeartbeats(ctx context.Context, v *viper.Viper, queueFilepath string) e
9091

9192
heartbeats := buildHeartbeats(ctx, params)
9293

93-
var chOfflineSave = make(chan bool)
94+
var (
95+
chOfflineSave = make(chan bool)
96+
savedOffline bool
97+
)
9498

9599
// only send at once the maximum amount of `offline.SendLimit`.
96100
if len(heartbeats) > offline.SendLimit {
101+
savedOffline = true
102+
97103
extraHeartbeats := heartbeats[offline.SendLimit:]
98104

99105
logger.Debugf("save %d extra heartbeat(s) to offline queue", len(extraHeartbeats))
@@ -130,7 +136,7 @@ func SendHeartbeats(ctx context.Context, v *viper.Viper, queueFilepath string) e
130136
results, err := handle(ctx, heartbeats)
131137

132138
// wait for offline queue save to finish
133-
if len(heartbeats) > offline.SendLimit {
139+
if savedOffline {
134140
<-chOfflineSave
135141
}
136142

@@ -157,7 +163,9 @@ func buildHandle(ctx context.Context, v *viper.Viper, params params.Params, queu
157163
return nil, err
158164
}
159165

160-
var handleOpts []heartbeat.HandleOption
166+
handleOpts := []heartbeat.HandleOption{
167+
filter.WithLengthValidator(),
168+
}
161169

162170
if !params.Offline.Disabled {
163171
handleOpts = append(handleOpts, offline.WithQueue(queueFilepath))
@@ -254,7 +262,6 @@ func initHandleOptions() []handler.Preprocessor {
254262
handler.WithProjectFiltering(),
255263
handler.WithHeartbeatSanitization(),
256264
handler.WithRemoteCleanup(),
257-
handler.WithLengthValidator(),
258265
}
259266
}
260267

cmd/offline/offline.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/wakatime/wakatime-cli/cmd/handler"
99
"github.com/wakatime/wakatime-cli/pkg/api"
10+
"github.com/wakatime/wakatime-cli/pkg/filter"
1011
"github.com/wakatime/wakatime-cli/pkg/heartbeat"
1112
"github.com/wakatime/wakatime-cli/pkg/log"
1213
"github.com/wakatime/wakatime-cli/pkg/offline"
@@ -40,7 +41,7 @@ func SaveHeartbeats(ctx context.Context, v *viper.Viper, heartbeats []heartbeat.
4041
}
4142

4243
handleOpts := initHandleOptions()
43-
sender := heartbeat.NewHandle(Noop{}, offline.WithQueue(queueFilepath))
44+
sender := heartbeat.NewHandle(Noop{}, filter.WithLengthValidator(), offline.WithQueue(queueFilepath))
4445
handle := handler.New(v, handler.Config{
4546
Params: params,
4647
ParamsLoader: LoadParams,
@@ -130,7 +131,6 @@ func initHandleOptions() []handler.Preprocessor {
130131
handler.WithProjectFiltering(),
131132
handler.WithHeartbeatSanitization(),
132133
handler.WithRemoteCleanup(),
133-
handler.WithLengthValidator(),
134134
}
135135
}
136136

main_test.go

Lines changed: 175 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,16 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
347347

348348
var numCalls int
349349

350+
projectFolder, err := filepath.Abs(".")
351+
require.NoError(t, err)
352+
353+
entityPath, err := realpath.Realpath("testdata/main.go")
354+
require.NoError(t, err)
355+
356+
entityPath = strings.ReplaceAll(entityPath, `\`, `/`)
357+
subfolders := project.CountSlashesInProjectFolder(projectFolder)
358+
userAgent := heartbeat.UserAgent(ctx, "")
359+
350360
router.HandleFunc("/users/current/heartbeats.bulk", func(w http.ResponseWriter, req *http.Request) {
351361
numCalls++
352362

@@ -361,9 +371,64 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
361371

362372
switch numCalls {
363373
case 1:
374+
// 1st request sends the main heartbeat + 24 extra heartbeats
364375
filename = "testdata/api_heartbeats_response_extra_heartbeats.json"
376+
377+
// check body
378+
expectedBodyTpl, err := os.ReadFile("testdata/api_heartbeats_request_extra_heartbeats_template.json")
379+
require.NoError(t, err)
380+
381+
expectedBody := fmt.Sprintf(
382+
string(expectedBodyTpl),
383+
entityPath, subfolders, userAgent,
384+
entityPath, subfolders, userAgent,
385+
entityPath, subfolders, userAgent,
386+
entityPath, subfolders, userAgent,
387+
entityPath, subfolders, userAgent,
388+
entityPath, subfolders, userAgent,
389+
entityPath, subfolders, userAgent,
390+
entityPath, subfolders, userAgent,
391+
entityPath, subfolders, userAgent,
392+
entityPath, subfolders, userAgent,
393+
entityPath, subfolders, userAgent,
394+
entityPath, subfolders, userAgent,
395+
entityPath, subfolders, userAgent,
396+
entityPath, subfolders, userAgent,
397+
entityPath, subfolders, userAgent,
398+
entityPath, subfolders, userAgent,
399+
entityPath, subfolders, userAgent,
400+
entityPath, subfolders, userAgent,
401+
entityPath, subfolders, userAgent,
402+
entityPath, subfolders, userAgent,
403+
entityPath, subfolders, userAgent,
404+
entityPath, subfolders, userAgent,
405+
entityPath, subfolders, userAgent,
406+
entityPath, subfolders, userAgent,
407+
entityPath, subfolders, userAgent,
408+
)
409+
410+
body, err := io.ReadAll(req.Body)
411+
require.NoError(t, err)
412+
413+
assert.JSONEq(t, expectedBody, string(body))
365414
case 2:
415+
// 2nd request sends the trimmed 2 extra heartbeats stored to the offline db
366416
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
417+
418+
// check body
419+
expectedBodyTpl, err := os.ReadFile("testdata/api_heartbeats_request_extra_heartbeats_extra_template.json")
420+
require.NoError(t, err)
421+
422+
expectedBody := fmt.Sprintf(
423+
string(expectedBodyTpl),
424+
entityPath, subfolders, userAgent,
425+
entityPath, subfolders, userAgent,
426+
)
427+
428+
body, err := io.ReadAll(req.Body)
429+
require.NoError(t, err)
430+
431+
assert.JSONEq(t, expectedBody, string(body))
367432
}
368433

369434
// write response
@@ -411,23 +476,25 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
411476
"--config", tmpConfigFile.Name(),
412477
"--internal-config", tmpInternalConfigFile.Name(),
413478
"--entity", "testdata/main.go",
479+
"--category", "coding",
414480
"--extra-heartbeats", "true",
415481
"--cursorpos", "12",
416-
"--sync-offline-activity", "2",
417482
"--offline-queue-file", offlineQueueFile.Name(),
418483
"--offline-queue-file-legacy", offlineQueueFileLegacy.Name(),
419484
"--lineno", "42",
420485
"--lines-in-file", "100",
421-
"--time", "1585598059",
486+
"--time", "1585598200",
422487
"--hide-branch-names", ".*",
488+
"--project", "wakatime-cli",
489+
"--project-folder", projectFolder,
423490
"--write",
424491
"--verbose",
425492
)
426493

427494
offlineCount, err := offline.CountHeartbeats(ctx, offlineQueueFile.Name())
428495
require.NoError(t, err)
429496

430-
assert.Equal(t, 1, offlineCount)
497+
assert.Zero(t, offlineCount)
431498

432499
assert.Eventually(t, func() bool { return numCalls == 2 }, time.Second, 50*time.Millisecond)
433500
}
@@ -573,8 +640,6 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)
573640
filename = "testdata/api_heartbeats_response_extra_heartbeats_legacy_offline.json"
574641
case 3:
575642
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
576-
case 4:
577-
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
578643
}
579644

580645
// write response
@@ -672,7 +737,111 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)
672737

673738
assert.Zero(t, offlineCount)
674739

675-
assert.Eventually(t, func() bool { return numCalls == 4 }, time.Second, 50*time.Millisecond)
740+
assert.Eventually(t, func() bool { return numCalls == 3 }, time.Second, 50*time.Millisecond)
741+
}
742+
743+
func TestSendHeartbeats_SyncOfflineActivity(t *testing.T) {
744+
apiURL, router, close := setupTestServer()
745+
defer close()
746+
747+
ctx := t.Context()
748+
749+
var numCalls int
750+
751+
router.HandleFunc("/users/current/heartbeats.bulk", func(w http.ResponseWriter, req *http.Request) {
752+
numCalls++
753+
754+
// check headers
755+
assert.Equal(t, http.MethodPost, req.Method)
756+
assert.Equal(t, []string{"application/json"}, req.Header["Accept"])
757+
assert.Equal(t, []string{"application/json"}, req.Header["Content-Type"])
758+
assert.Equal(t, []string{"Basic MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw"}, req.Header["Authorization"])
759+
assert.Equal(t, []string{heartbeat.UserAgent(ctx, "")}, req.Header["User-Agent"])
760+
761+
// write response
762+
f, err := os.Open("testdata/api_heartbeats_response_offline.json")
763+
require.NoError(t, err)
764+
765+
w.WriteHeader(http.StatusCreated)
766+
_, err = io.Copy(w, f)
767+
require.NoError(t, err)
768+
})
769+
770+
tmpDir := t.TempDir()
771+
772+
// create legacy offline queue file and add some heartbeats
773+
offlineQueueFileLegacy, err := os.CreateTemp(tmpDir, "legacy-offline-file")
774+
require.NoError(t, err)
775+
776+
// close to avoid "The process cannot access the file because it is being used by another process" error on Windows
777+
offlineQueueFileLegacy.Close()
778+
779+
offlineQueueFile, err := os.CreateTemp(tmpDir, "new-offline-file")
780+
require.NoError(t, err)
781+
782+
defer offlineQueueFile.Close()
783+
784+
db, err := bolt.Open(offlineQueueFile.Name(), 0600, nil)
785+
require.NoError(t, err)
786+
787+
dataGo, err := os.ReadFile("testdata/heartbeat_go.json")
788+
require.NoError(t, err)
789+
790+
dataPy, err := os.ReadFile("testdata/heartbeat_py.json")
791+
require.NoError(t, err)
792+
793+
dataJs, err := os.ReadFile("testdata/heartbeat_js.json")
794+
require.NoError(t, err)
795+
796+
insertHeartbeatRecords(t, db, "heartbeats", []heartbeatRecord{
797+
{
798+
ID: "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
799+
Heartbeat: string(dataGo),
800+
},
801+
{
802+
ID: "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false",
803+
Heartbeat: string(dataPy),
804+
},
805+
{
806+
ID: "1592868394.084354-file-building-wakatime-todaygoal-/tmp/main.js-false",
807+
Heartbeat: string(dataJs),
808+
},
809+
})
810+
811+
err = db.Close()
812+
require.NoError(t, err)
813+
814+
tmpConfigFile, err := os.CreateTemp(tmpDir, "wakatime.cfg")
815+
require.NoError(t, err)
816+
817+
defer tmpConfigFile.Close()
818+
819+
tmpInternalConfigFile, err := os.CreateTemp(tmpDir, "wakatime-internal.cfg")
820+
require.NoError(t, err)
821+
822+
defer tmpInternalConfigFile.Close()
823+
824+
runWakatimeCli(
825+
t,
826+
&bytes.Buffer{},
827+
"--api-url", apiURL,
828+
"--key", "00000000-0000-4000-8000-000000000000",
829+
"--config", tmpConfigFile.Name(),
830+
"--internal-config", tmpInternalConfigFile.Name(),
831+
"--sync-offline-activity", "3",
832+
"--offline-queue-file", offlineQueueFile.Name(),
833+
"--offline-queue-file-legacy", offlineQueueFileLegacy.Name(),
834+
"--verbose",
835+
)
836+
837+
assert.NoFileExists(t, offlineQueueFileLegacy.Name())
838+
839+
offlineCount, err := offline.CountHeartbeats(ctx, offlineQueueFile.Name())
840+
require.NoError(t, err)
841+
842+
assert.Zero(t, offlineCount)
843+
844+
assert.Eventually(t, func() bool { return numCalls == 1 }, time.Second, 50*time.Millisecond)
676845
}
677846

678847
func TestSendHeartbeats_Err(t *testing.T) {
@@ -977,7 +1146,6 @@ func TestSendHeartbeats_OmitEmptyCategory(t *testing.T) {
9771146
"--internal-config", tmpInternalConfigFile.Name(),
9781147
"--entity", filepath.Join(tmpDir, "main.go"),
9791148
"--cursorpos", "12",
980-
"--sync-offline-activity", "2",
9811149
"--offline-queue-file", offlineQueueFile.Name(),
9821150
"--offline-queue-file-legacy", offlineQueueFileLegacy.Name(),
9831151
"--lineno", "42",

pkg/offline/offline.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,6 @@ func Sync(ctx context.Context, filepath string, syncLimit int) func(next heartbe
132132
run int
133133
)
134134

135-
if syncLimit == 0 {
136-
syncLimit = math.MaxInt32
137-
}
138-
139135
logger := log.Extract(ctx)
140136

141137
for {

pkg/offline/offline_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"math"
89
"net/http"
910
"os"
1011
"path/filepath"
@@ -862,7 +863,7 @@ func TestSync_SyncUnlimited(t *testing.T) {
862863
err = db.Close()
863864
require.NoError(t, err)
864865

865-
syncFn := offline.Sync(t.Context(), f.Name(), 0)
866+
syncFn := offline.Sync(t.Context(), f.Name(), math.MaxInt32)
866867

867868
var numCalls int
868869

0 commit comments

Comments
 (0)