@@ -347,6 +347,16 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
347
347
348
348
var numCalls int
349
349
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
+
350
360
router .HandleFunc ("/users/current/heartbeats.bulk" , func (w http.ResponseWriter , req * http.Request ) {
351
361
numCalls ++
352
362
@@ -361,9 +371,64 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
361
371
362
372
switch numCalls {
363
373
case 1 :
374
+ // 1st request sends the main heartbeat + 24 extra heartbeats
364
375
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 ))
365
414
case 2 :
415
+ // 2nd request sends the trimmed 2 extra heartbeats stored to the offline db
366
416
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 ))
367
432
}
368
433
369
434
// write response
@@ -411,23 +476,25 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
411
476
"--config" , tmpConfigFile .Name (),
412
477
"--internal-config" , tmpInternalConfigFile .Name (),
413
478
"--entity" , "testdata/main.go" ,
479
+ "--category" , "coding" ,
414
480
"--extra-heartbeats" , "true" ,
415
481
"--cursorpos" , "12" ,
416
- "--sync-offline-activity" , "2" ,
417
482
"--offline-queue-file" , offlineQueueFile .Name (),
418
483
"--offline-queue-file-legacy" , offlineQueueFileLegacy .Name (),
419
484
"--lineno" , "42" ,
420
485
"--lines-in-file" , "100" ,
421
- "--time" , "1585598059 " ,
486
+ "--time" , "1585598200 " ,
422
487
"--hide-branch-names" , ".*" ,
488
+ "--project" , "wakatime-cli" ,
489
+ "--project-folder" , projectFolder ,
423
490
"--write" ,
424
491
"--verbose" ,
425
492
)
426
493
427
494
offlineCount , err := offline .CountHeartbeats (ctx , offlineQueueFile .Name ())
428
495
require .NoError (t , err )
429
496
430
- assert .Equal ( t , 1 , offlineCount )
497
+ assert .Zero ( t , offlineCount )
431
498
432
499
assert .Eventually (t , func () bool { return numCalls == 2 }, time .Second , 50 * time .Millisecond )
433
500
}
@@ -573,8 +640,6 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)
573
640
filename = "testdata/api_heartbeats_response_extra_heartbeats_legacy_offline.json"
574
641
case 3 :
575
642
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
576
- case 4 :
577
- filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
578
643
}
579
644
580
645
// write response
@@ -672,7 +737,111 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)
672
737
673
738
assert .Zero (t , offlineCount )
674
739
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 )
676
845
}
677
846
678
847
func TestSendHeartbeats_Err (t * testing.T ) {
@@ -977,7 +1146,6 @@ func TestSendHeartbeats_OmitEmptyCategory(t *testing.T) {
977
1146
"--internal-config" , tmpInternalConfigFile .Name (),
978
1147
"--entity" , filepath .Join (tmpDir , "main.go" ),
979
1148
"--cursorpos" , "12" ,
980
- "--sync-offline-activity" , "2" ,
981
1149
"--offline-queue-file" , offlineQueueFile .Name (),
982
1150
"--offline-queue-file-legacy" , offlineQueueFileLegacy .Name (),
983
1151
"--lineno" , "42" ,
0 commit comments