mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-25 16:42:28 +00:00
porting - part 5 done
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"domain": "garmin.com",
|
||||
"username": "fbleagh",
|
||||
"auth_token": "bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpLW9hdXRoLXNpZ25lci1wcm9kLTIwMjQtcTEifQ.eyJzY29wZSI6WyJBVFBfUkVBRCIsIkFUUF9XUklURSIsIkNPTU1VTklUWV9DT1VSU0VfUkVBRCIsIkNPTU1VTklUWV9DT1VSU0VfV1JJVEUiLCJDT05ORUNUX01DVF9EQUlMWV9MT0dfUkVBRCIsIkNPTk5FQ1RfUkVBRCIsIkNPTk5FQ1RfV1JJVEUiLCJESVZFX0FQSV9SRUFEIiwiRElfT0FVVEhfMl9BVVRIT1JJWkFUSU9OX0NPREVfQ1JFQVRFIiwiRFRfQ0xJRU5UX0FOQUxZVElDU19XUklURSIsIkdBUk1JTlBBWV9SRUFEIiwiR0FSTUlOUEFZX1dSSVRFIiwiR0NPRkZFUl9SRUFEIiwiR0NPRkZFUl9XUklURSIsIkdIU19TQU1EIiwiR0hTX1VQTE9BRCIsIkdPTEZfQVBJX1JFQUQiLCJHT0xGX0FQSV9XUklURSIsIklOU0lHSFRTX1JFQUQiLCJJTlNJR0hUU19XUklURSIsIk9NVF9DQU1QQUlHTl9SRUFEIiwiT01UX1NVQlNDUklQVElPTl9SRUFEIiwiUFJPRFVDVF9TRUFSQ0hfUkVBRCJdLCJpc3MiOiJodHRwczovL2RpYXV0aC5nYXJtaW4uY29tIiwicmV2b2NhdGlvbl9lbGlnaWJpbGl0eSI6WyJHTE9CQUxfU0lHTk9VVCIsIk1BTkFHRURfU1RBVFVTIl0sImNsaWVudF90eXBlIjoiVU5ERUZJTkVEIiwiZXhwIjoxNzU3MzYyMzI3LCJpYXQiOjE3NTcyNzI0NDEsImdhcm1pbl9ndWlkIjoiNTZlZTk1ZmMtMzY0MC00NGU2LTg1ODAtNDc4NDEwZDQwZGFhIiwianRpIjoiZjVmYzFhMzAtZGVkZi00N2FmLTg5YjgtM2QwNjFjZjkxMTMxIiwiY2xpZW50X2lkIjoiR0FSTUlOX0NPTk5FQ1RfTU9CSUxFX0FORFJPSURfREkifQ.cdgNSDtkYnySkPdHTHxvck3BZXVZ0H6mGcqU0fJqqRO5cuh0_exgGM_VBLxoos_MqEYeryZqkw__UfwA1dvamoClooPpUFZIcPmsTl_uSILd8IIiWFjhgXJnTybE3mI_hPEaILzWnVDzQX4lv1K_oTzCVx0I7moonRAk3mbccKpj_kWcIm-CFVbuGbApTCJzRoOr46yFPUnbOxeA0eJl8BbPFmPWK0z_FvcLS8q7ZKuksBWW2gorQovqesIG63k-wK1PFOvm2EDosSFW0RTCFY7cBMx3nz_f7jFG9E5qt971z8EcKCq83pWs2CHIqy64KkVoub3CD0LQRKIjilNsEA"
|
||||
"auth_token": "bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpLW9hdXRoLXNpZ25lci1wcm9kLTIwMjQtcTEifQ.eyJzY29wZSI6WyJBVFBfUkVBRCIsIkFUUF9XUklURSIsIkNPTU1VTklUWV9DT1VSU0VfUkVBRCIsIkNPTU1VTklUWV9DT1VSU0VfV1JJVEUiLCJDT05ORUNUX01DVF9EQUlMWV9MT0dfUkVBRCIsIkNPTk5FQ1RfUkVBRCIsIkNPTk5FQ1RfV1JJVEUiLCJESVZFX0FQSV9SRUFEIiwiRElfT0FVVEhfMl9BVVRIT1JJWkFUSU9OX0NPREVfQ1JFQVRFIiwiRFRfQ0xJRU5UX0FOQUxZVElDU19XUklURSIsIkdBUk1JTlBBWV9SRUFEIiwiR0FSTUlOUEFZX1dSSVRFIiwiR0NPRkZFUl9SRUFEIiwiR0NPRkZFUl9XUklURSIsIkdIU19TQU1EIiwiR0hTX1VQTE9BRCIsIkdPTEZfQVBJX1JFQUQiLCJHT0xGX0FQSV9XUklURSIsIklOU0lHSFRTX1JFQUQiLCJJTlNJR0hUU19XUklURSIsIk9NVF9DQU1QQUlHTl9SRUFEIiwiT01UX1NVQlNDUklQVElPTl9SRUFEIiwiUFJPRFVDVF9TRUFSQ0hfUkVBRCJdLCJpc3MiOiJodHRwczovL2RpYXV0aC5nYXJtaW4uY29tIiwicmV2b2NhdGlvbl9lbGlnaWJpbGl0eSI6WyJHTE9CQUxfU0lHTk9VVCIsIk1BTkFHRURfU1RBVFVTIl0sImNsaWVudF90eXBlIjoiVU5ERUZJTkVEIiwiZXhwIjoxNzU3MzgwNTAyLCJpYXQiOjE3NTcyNzcwMjAsImdhcm1pbl9ndWlkIjoiNTZlZTk1ZmMtMzY0MC00NGU2LTg1ODAtNDc4NDEwZDQwZGFhIiwianRpIjoiZWVkMmQ2NTYtYWM0MC00NDdhLTkwYzEtOWMwMmQzNTM3MzZiIiwiY2xpZW50X2lkIjoiR0FSTUlOX0NPTk5FQ1RfTU9CSUxFX0FORFJPSURfREkifQ.HwBbJysBlmSHtPLPtZ1SITqa6jZ8SFBuej7j--iYblHqgKwM7preEM03FgVJUusQi9SarB_lID-pjNdJ6MRGnYo3NSzO4wnmalmnoAxn-9pvAYHKznCIq5x4exC-2SlvW4paNK_-UzOd9mp23FNCvXcLGOc_lJlFuC20YAQID9x3ujIGm3PBWp6ycWIRydyvnNZga-a2opaZPjvC1TXKycNsUY1qZSc4hj3D7_wFrMtYuu2HuGQFeyNRNInA6Ir-J3i_OBFl-L4tM3CvpJAGUU7VEY257x8M6YvHL7xER0tv3FjTdqwLAkSMkBP9qH1mq7zPrVp6JEIqghI-EQVwsA"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"domain": "garmin.com",
|
||||
"username": "fbleagh",
|
||||
"auth_token": "bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpLW9hdXRoLXNpZ25lci1wcm9kLTIwMjQtcTEifQ.eyJzY29wZSI6WyJBVFBfUkVBRCIsIkFUUF9XUklURSIsIkNPTU1VTklUWV9DT1VSU0VfUkVBRCIsIkNPTU1VTklUWV9DT1VSU0VfV1JJVEUiLCJDT05ORUNUX01DVF9EQUlMWV9MT0dfUkVBRCIsIkNPTk5FQ1RfUkVBRCIsIkNPTk5FQ1RfV1JJVEUiLCJESVZFX0FQSV9SRUFEIiwiRElfT0FVVEhfMl9BVVRIT1JJWkFUSU9OX0NPREVfQ1JFQVRFIiwiRFRfQ0xJRU5UX0FOQUxZVElDU19XUklURSIsIkdBUk1JTlBBWV9SRUFEIiwiR0FSTUlOUEFZX1dSSVRFIiwiR0NPRkZFUl9SRUFEIiwiR0NPRkZFUl9XUklURSIsIkdIU19TQU1EIiwiR0hTX1VQTE9BRCIsIkdPTEZfQVBJX1JFQUQiLCJHT0xGX0FQSV9XUklURSIsIklOU0lHSFRTX1JFQUQiLCJJTlNJR0hUU19XUklURSIsIk9NVF9DQU1QQUlHTl9SRUFEIiwiT01UX1NVQlNDUklQVElPTl9SRUFEIiwiUFJPRFVDVF9TRUFSQ0hfUkVBRCJdLCJpc3MiOiJodHRwczovL2RpYXV0aC5nYXJtaW4uY29tIiwicmV2b2NhdGlvbl9lbGlnaWJpbGl0eSI6WyJHTE9CQUxfU0lHTk9VVCIsIk1BTkFHRURfU1RBVFVTIl0sImNsaWVudF90eXBlIjoiVU5ERUZJTkVEIiwiZXhwIjoxNzU3MzMxMzUyLCJpYXQiOjE3NTcyNTg4NjUsImdhcm1pbl9ndWlkIjoiNTZlZTk1ZmMtMzY0MC00NGU2LTg1ODAtNDc4NDEwZDQwZGFhIiwianRpIjoiYTExYThkOGUtZTk3NS00ZmYzLWI5ZGUtMTgxNDRlZmI3NDIwIiwiY2xpZW50X2lkIjoiR0FSTUlOX0NPTk5FQ1RfTU9CSUxFX0FORFJPSURfREkifQ.aHN1exAr4j-_rjZ06OS9D7o4CpDYR1l09geAK6jh-NmJl4dsy3vWM1sv6c-9yiIO8FmuKhvBA44yKAhFCWOTNIJ3yUG-t8IFbYRMrPxBW4WZ4zqMN8XgVPI9Z_iLR0cEP6AaaAtzpVMWcwHn8wLhDUrpLMeCz7n8jMU0S-caXkByCa4zF1PhVs69hYH89Yn48lA_bFiJtbgx0aINnuu-0JHCj22NRjBTKPGBDcQg2fNapCrHoqZ1y-5BOfyB96u6VFXZZ6JNd-ar1EaVOw4G7zUhQCCDeilqjwQB68yIvbWoOhAyda93yB-_AcBU3wHrGHUaYqULEPSRex8zPxYH7A"
|
||||
"auth_token": "bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpLW9hdXRoLXNpZ25lci1wcm9kLTIwMjQtcTEifQ.eyJzY29wZSI6WyJBVFBfUkVBRCIsIkFUUF9XUklURSIsIkNPTU1VTklUWV9DT1VSU0VfUkVBRCIsIkNPTU1VTklUWV9DT1VSU0VfV1JJVEUiLCJDT05ORUNUX01DVF9EQUlMWV9MT0dfUkVBRCIsIkNPTk5FQ1RfUkVBRCIsIkNPTk5FQ1RfV1JJVEUiLCJESVZFX0FQSV9SRUFEIiwiRElfT0FVVEhfMl9BVVRIT1JJWkFUSU9OX0NPREVfQ1JFQVRFIiwiRFRfQ0xJRU5UX0FOQUxZVElDU19XUklURSIsIkdBUk1JTlBBWV9SRUFEIiwiR0FSTUlOUEFZX1dSSVRFIiwiR0NPRkZFUl9SRUFEIiwiR0NPRkZFUl9XUklURSIsIkdIU19TQU1EIiwiR0hTX1VQTE9BRCIsIkdPTEZfQVBJX1JFQUQiLCJHT0xGX0FQSV9XUklURSIsIklOU0lHSFRTX1JFQUQiLCJJTlNJR0hUU19XUklURSIsIk9NVF9DQU1QQUlHTl9SRUFEIiwiT01UX1NVQlNDUklQVElPTl9SRUFEIiwiUFJPRFVDVF9TRUFSQ0hfUkVBRCJdLCJpc3MiOiJodHRwczovL2RpYXV0aC5nYXJtaW4uY29tIiwicmV2b2NhdGlvbl9lbGlnaWJpbGl0eSI6WyJHTE9CQUxfU0lHTk9VVCIsIk1BTkFHRURfU1RBVFVTIl0sImNsaWVudF90eXBlIjoiVU5ERUZJTkVEIiwiZXhwIjoxNzU3MzYyMzI3LCJpYXQiOjE3NTcyNzI0NDEsImdhcm1pbl9ndWlkIjoiNTZlZTk1ZmMtMzY0MC00NGU2LTg1ODAtNDc4NDEwZDQwZGFhIiwianRpIjoiZjVmYzFhMzAtZGVkZi00N2FmLTg5YjgtM2QwNjFjZjkxMTMxIiwiY2xpZW50X2lkIjoiR0FSTUlOX0NPTk5FQ1RfTU9CSUxFX0FORFJPSURfREkifQ.cdgNSDtkYnySkPdHTHxvck3BZXVZ0H6mGcqU0fJqqRO5cuh0_exgGM_VBLxoos_MqEYeryZqkw__UfwA1dvamoClooPpUFZIcPmsTl_uSILd8IIiWFjhgXJnTybE3mI_hPEaILzWnVDzQX4lv1K_oTzCVx0I7moonRAk3mbccKpj_kWcIm-CFVbuGbApTCJzRoOr46yFPUnbOxeA0eJl8BbPFmPWK0z_FvcLS8q7ZKuksBWW2gorQovqesIG63k-wK1PFOvm2EDosSFW0RTCFY7cBMx3nz_f7jFG9E5qt971z8EcKCq83pWs2CHIqy64KkVoub3CD0LQRKIjilNsEA"
|
||||
}
|
||||
@@ -67,8 +67,8 @@ func (b *BaseData) Get(day time.Time, c *client.Client) (interface{}, error) {
|
||||
// Returns:
|
||||
//
|
||||
// []interface{}: Slice of results (order matches date range)
|
||||
// error: First error encountered during processing, if any
|
||||
func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers int) ([]interface{}, error) {
|
||||
// []error: Slice of errors encountered during processing
|
||||
func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers int) ([]interface{}, []error) {
|
||||
if maxWorkers < 1 {
|
||||
maxWorkers = 1
|
||||
}
|
||||
@@ -82,8 +82,7 @@ func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers in
|
||||
var wg sync.WaitGroup
|
||||
workCh := make(chan time.Time, days)
|
||||
resultsCh := make(chan interface{}, days)
|
||||
errCh := make(chan error, 1)
|
||||
done := make(chan bool)
|
||||
errCh := make(chan error, days)
|
||||
|
||||
// Worker function
|
||||
worker := func() {
|
||||
@@ -91,11 +90,8 @@ func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers in
|
||||
for date := range workCh {
|
||||
result, err := b.Get(date, c)
|
||||
if err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
default:
|
||||
}
|
||||
return
|
||||
errCh <- err
|
||||
continue
|
||||
}
|
||||
resultsCh <- result
|
||||
}
|
||||
@@ -115,28 +111,34 @@ func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers in
|
||||
close(workCh)
|
||||
}()
|
||||
|
||||
// Close results channel when all workers finish
|
||||
// Close channels when all workers finish
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultsCh)
|
||||
done <- true
|
||||
close(errCh)
|
||||
}()
|
||||
|
||||
// Collect results
|
||||
// Collect results and errors
|
||||
var results []interface{}
|
||||
var err error
|
||||
var errs []error
|
||||
|
||||
collect:
|
||||
for {
|
||||
// Collect results until both channels are closed
|
||||
for resultsCh != nil || errCh != nil {
|
||||
select {
|
||||
case result := <-resultsCh:
|
||||
case result, ok := <-resultsCh:
|
||||
if !ok {
|
||||
resultsCh = nil
|
||||
continue
|
||||
}
|
||||
results = append(results, result)
|
||||
case err = <-errCh:
|
||||
break collect
|
||||
case <-done:
|
||||
break collect
|
||||
case err, ok := <-errCh:
|
||||
if !ok {
|
||||
errCh = nil
|
||||
continue
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return results, err
|
||||
return results, errs
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ func TestBaseData_List(t *testing.T) {
|
||||
maxWorkers := 3
|
||||
|
||||
// Execute
|
||||
results, err := mockData.List(end, days, c, maxWorkers)
|
||||
results, errs := mockData.List(end, days, c, maxWorkers)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, errs)
|
||||
assert.Len(t, results, days)
|
||||
assert.Contains(t, results, "data for 2023-06-15")
|
||||
assert.Contains(t, results, "data for 2023-06-11")
|
||||
@@ -65,10 +65,10 @@ func TestBaseData_List_ErrorHandling(t *testing.T) {
|
||||
maxWorkers := 2
|
||||
|
||||
// Execute
|
||||
results, err := mockData.List(end, days, c, maxWorkers)
|
||||
results, errs := mockData.List(end, days, c, maxWorkers)
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "bad luck day", err.Error())
|
||||
assert.Len(t, results, 4) // Should have some results before error
|
||||
assert.Len(t, errs, 1)
|
||||
assert.Equal(t, "bad luck day", errs[0].Error())
|
||||
assert.Len(t, results, 4) // Should have results for non-error days
|
||||
}
|
||||
|
||||
120
garth/data/body_battery.go
Normal file
120
garth/data/body_battery.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"garmin-connect/garth/client"
|
||||
)
|
||||
|
||||
// DailyBodyBatteryStress represents complete daily Body Battery and stress data
|
||||
type DailyBodyBatteryStress struct {
|
||||
UserProfilePK int `json:"userProfilePk"`
|
||||
CalendarDate time.Time `json:"calendarDate"`
|
||||
StartTimestampGMT time.Time `json:"startTimestampGmt"`
|
||||
EndTimestampGMT time.Time `json:"endTimestampGmt"`
|
||||
StartTimestampLocal time.Time `json:"startTimestampLocal"`
|
||||
EndTimestampLocal time.Time `json:"endTimestampLocal"`
|
||||
MaxStressLevel int `json:"maxStressLevel"`
|
||||
AvgStressLevel int `json:"avgStressLevel"`
|
||||
StressChartValueOffset int `json:"stressChartValueOffset"`
|
||||
StressChartYAxisOrigin int `json:"stressChartYAxisOrigin"`
|
||||
StressValuesArray [][]int `json:"stressValuesArray"`
|
||||
BodyBatteryValuesArray [][]any `json:"bodyBatteryValuesArray"`
|
||||
}
|
||||
|
||||
// BodyBatteryEvent represents a Body Battery impact event
|
||||
type BodyBatteryEvent struct {
|
||||
EventType string `json:"eventType"`
|
||||
EventStartTimeGMT time.Time `json:"eventStartTimeGmt"`
|
||||
TimezoneOffset int `json:"timezoneOffset"`
|
||||
DurationInMilliseconds int `json:"durationInMilliseconds"`
|
||||
BodyBatteryImpact int `json:"bodyBatteryImpact"`
|
||||
FeedbackType string `json:"feedbackType"`
|
||||
ShortFeedback string `json:"shortFeedback"`
|
||||
}
|
||||
|
||||
// BodyBatteryData represents legacy Body Battery events data
|
||||
type BodyBatteryData struct {
|
||||
Event *BodyBatteryEvent `json:"event"`
|
||||
ActivityName string `json:"activityName"`
|
||||
ActivityType string `json:"activityType"`
|
||||
ActivityID string `json:"activityId"`
|
||||
AverageStress float64 `json:"averageStress"`
|
||||
StressValuesArray [][]int `json:"stressValuesArray"`
|
||||
BodyBatteryValuesArray [][]any `json:"bodyBatteryValuesArray"`
|
||||
}
|
||||
|
||||
// BodyBatteryReading represents an individual Body Battery reading
|
||||
type BodyBatteryReading struct {
|
||||
Timestamp int `json:"timestamp"`
|
||||
Status string `json:"status"`
|
||||
Level int `json:"level"`
|
||||
Version float64 `json:"version"`
|
||||
}
|
||||
|
||||
// StressReading represents an individual stress reading
|
||||
type StressReading struct {
|
||||
Timestamp int `json:"timestamp"`
|
||||
StressLevel int `json:"stressLevel"`
|
||||
}
|
||||
|
||||
// ParseBodyBatteryReadings converts body battery values array to structured readings
|
||||
func ParseBodyBatteryReadings(valuesArray [][]any) []BodyBatteryReading {
|
||||
readings := make([]BodyBatteryReading, 0)
|
||||
for _, values := range valuesArray {
|
||||
if len(values) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
timestamp, ok1 := values[0].(int)
|
||||
status, ok2 := values[1].(string)
|
||||
level, ok3 := values[2].(int)
|
||||
version, ok4 := values[3].(float64)
|
||||
|
||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
||||
continue
|
||||
}
|
||||
|
||||
readings = append(readings, BodyBatteryReading{
|
||||
Timestamp: timestamp,
|
||||
Status: status,
|
||||
Level: level,
|
||||
Version: version,
|
||||
})
|
||||
}
|
||||
sort.Slice(readings, func(i, j int) bool {
|
||||
return readings[i].Timestamp < readings[j].Timestamp
|
||||
})
|
||||
return readings
|
||||
}
|
||||
|
||||
// ParseStressReadings converts stress values array to structured readings
|
||||
func ParseStressReadings(valuesArray [][]int) []StressReading {
|
||||
readings := make([]StressReading, 0)
|
||||
for _, values := range valuesArray {
|
||||
if len(values) != 2 {
|
||||
continue
|
||||
}
|
||||
readings = append(readings, StressReading{
|
||||
Timestamp: values[0],
|
||||
StressLevel: values[1],
|
||||
})
|
||||
}
|
||||
sort.Slice(readings, func(i, j int) bool {
|
||||
return readings[i].Timestamp < readings[j].Timestamp
|
||||
})
|
||||
return readings
|
||||
}
|
||||
|
||||
// Get implements the Data interface for DailyBodyBatteryStress
|
||||
func (d *DailyBodyBatteryStress) Get(day time.Time, client *client.Client) (any, error) {
|
||||
// Implementation to be added
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// List implements the Data interface for concurrent fetching
|
||||
func (d *DailyBodyBatteryStress) List(end time.Time, days int, client *client.Client, maxWorkers int) ([]any, error) {
|
||||
// Implementation to be added
|
||||
return []any{}, nil
|
||||
}
|
||||
122
garth/data/body_battery_test.go
Normal file
122
garth/data/body_battery_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseBodyBatteryReadings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input [][]any
|
||||
expected []BodyBatteryReading
|
||||
}{
|
||||
{
|
||||
name: "valid readings",
|
||||
input: [][]any{
|
||||
{1000, "ACTIVE", 75, 1.0},
|
||||
{2000, "ACTIVE", 70, 1.0},
|
||||
{3000, "REST", 65, 1.0},
|
||||
},
|
||||
expected: []BodyBatteryReading{
|
||||
{1000, "ACTIVE", 75, 1.0},
|
||||
{2000, "ACTIVE", 70, 1.0},
|
||||
{3000, "REST", 65, 1.0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid readings",
|
||||
input: [][]any{
|
||||
{1000, "ACTIVE", 75}, // missing version
|
||||
{2000, "ACTIVE"}, // missing level and version
|
||||
{3000}, // only timestamp
|
||||
{"invalid", "ACTIVE", 75, 1.0}, // wrong timestamp type
|
||||
},
|
||||
expected: []BodyBatteryReading{},
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: [][]any{},
|
||||
expected: []BodyBatteryReading{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ParseBodyBatteryReadings(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStressReadings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input [][]int
|
||||
expected []StressReading
|
||||
}{
|
||||
{
|
||||
name: "valid readings",
|
||||
input: [][]int{
|
||||
{1000, 25},
|
||||
{2000, 30},
|
||||
{3000, 20},
|
||||
},
|
||||
expected: []StressReading{
|
||||
{1000, 25},
|
||||
{2000, 30},
|
||||
{3000, 20},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid readings",
|
||||
input: [][]int{
|
||||
{1000}, // missing stress level
|
||||
{2000, 30, 1}, // extra value
|
||||
{}, // empty
|
||||
},
|
||||
expected: []StressReading{},
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: [][]int{},
|
||||
expected: []StressReading{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ParseStressReadings(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDailyBodyBatteryStress(t *testing.T) {
|
||||
now := time.Now()
|
||||
d := DailyBodyBatteryStress{
|
||||
CalendarDate: now,
|
||||
BodyBatteryValuesArray: [][]any{
|
||||
{1000, "ACTIVE", 75, 1.0},
|
||||
{2000, "ACTIVE", 70, 1.0},
|
||||
},
|
||||
StressValuesArray: [][]int{
|
||||
{1000, 25},
|
||||
{2000, 30},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("body battery readings", func(t *testing.T) {
|
||||
readings := ParseBodyBatteryReadings(d.BodyBatteryValuesArray)
|
||||
assert.Len(t, readings, 2)
|
||||
assert.Equal(t, 75, readings[0].Level)
|
||||
})
|
||||
|
||||
t.Run("stress readings", func(t *testing.T) {
|
||||
readings := ParseStressReadings(d.StressValuesArray)
|
||||
assert.Len(t, readings, 2)
|
||||
assert.Equal(t, 25, readings[0].StressLevel)
|
||||
})
|
||||
}
|
||||
7
go.mod
7
go.mod
@@ -4,4 +4,9 @@ go 1.24.2
|
||||
|
||||
require github.com/joho/godotenv v1.5.1
|
||||
|
||||
require github.com/stretchr/testify v1.11.1 // indirect
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
8
go.sum
8
go.sum
@@ -1,4 +1,12 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
Reference in New Issue
Block a user