mirror of
https://github.com/sstent/go-garminconnect.git
synced 2026-02-08 07:21:47 +00:00
ficing all the build errors - checkpoint 1
This commit is contained in:
@@ -3,215 +3,112 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sstent/go-garminconnect/internal/auth/garth"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TEST PROGRESS:
|
|
||||||
// - [ ] Move ValidateFIT to internal/fit package
|
|
||||||
// - [ ] Create unified mock server implementation
|
|
||||||
// - [ ] Extend mock server for upload handler
|
|
||||||
// - [ ] Remove ValidateFIT from this file
|
|
||||||
// - [ ] Create shared test helper package
|
|
||||||
|
|
||||||
// TestGetActivities is now part of table-driven tests below
|
|
||||||
|
|
||||||
func TestActivitiesEndpoints(t *testing.T) {
|
func TestActivitiesEndpoints(t *testing.T) {
|
||||||
// Create mock server
|
|
||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
// Create a mock session
|
// Setup standard mock handlers
|
||||||
session := &garth.Session{OAuth2Token: "test-token"}
|
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
activities := []ActivityResponse{{
|
||||||
|
ActivityID: 1,
|
||||||
|
Name: "Morning Run",
|
||||||
|
}}
|
||||||
|
json.NewEncoder(w).Encode(ActivitiesResponse{
|
||||||
|
Activities: activities,
|
||||||
|
Pagination: Pagination{Page: 1, PageSize: 10, TotalCount: 1},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Create client with mock server URL and session
|
mockServer.SetActivityDetailsHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
client, err := NewClient(session, "")
|
pathParts := strings.Split(r.URL.Path, "/")
|
||||||
if err != nil {
|
if len(pathParts) < 2 {
|
||||||
t.Fatalf("failed to create client: %v", err)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
return
|
||||||
client.HTTPClient.SetBaseURL(mockServer.URL())
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
activityID, err := strconv.ParseInt(pathParts[len(pathParts)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(ActivityDetailResponse{
|
||||||
|
ActivityResponse: ActivityResponse{
|
||||||
|
ActivityID: activityID,
|
||||||
|
Name: "Mock Activity",
|
||||||
|
Type: "RUNNING",
|
||||||
|
StartTime: garminTime{time.Now().Add(-24 * time.Hour)},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
mockServer.SetUploadHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"activityId": 12345})
|
||||||
|
})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
testFunc func(t *testing.T, client *Client)
|
setup func()
|
||||||
description string
|
testFunc func(t *testing.T)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "GetActivitiesSuccess",
|
name: "GetActivitiesSuccess",
|
||||||
description: "Test successful activity list retrieval",
|
testFunc: func(t *testing.T) {
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
activities, _, err := client.GetActivities(context.Background(), 1, 10)
|
||||||
activities, pagination, err := client.GetActivities(context.Background(), 1, 10)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, activities, 1)
|
assert.Len(t, activities, 1)
|
||||||
assert.Equal(t, int64(1), activities[0].ActivityID)
|
assert.Equal(t, int64(1), activities[0].ActivityID)
|
||||||
assert.Equal(t, "Morning Run", activities[0].Name)
|
|
||||||
assert.Equal(t, 1, pagination.Page)
|
|
||||||
assert.Equal(t, 10, pagination.PageSize)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GetActivityDetailsSuccess",
|
name: "GetActivityDetailsSuccess",
|
||||||
description: "Test successful activity details retrieval",
|
testFunc: func(t *testing.T) {
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
activity, err := client.GetActivityDetails(context.Background(), 1)
|
activity, err := client.GetActivityDetails(context.Background(), 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(1), activity.ActivityID)
|
assert.Equal(t, int64(1), activity.ActivityID)
|
||||||
assert.Equal(t, "Mock Activity", activity.Name)
|
|
||||||
assert.Equal(t, 150, activity.AverageHR)
|
|
||||||
assert.Equal(t, "RUNNING", activity.Type)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GetActivitiesServerError",
|
name: "GetActivitiesServerError",
|
||||||
description: "Test server error handling for activity list",
|
setup: func() {
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
testFunc: func(t *testing.T) {
|
||||||
_, _, err := client.GetActivities(context.Background(), 1, 10)
|
_, _, err := client.GetActivities(context.Background(), 1, 10)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "failed to get activities")
|
assert.Contains(t, err.Error(), "failed to get activities")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GetActivityDetailsNotFound",
|
name: "UploadActivitySuccess",
|
||||||
description: "Test not found error for activity details",
|
testFunc: func(t *testing.T) {
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
})
|
|
||||||
_, err := client.GetActivityDetails(context.Background(), 999)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "resource not found")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivitiesInvalidPagination",
|
|
||||||
description: "Test invalid pagination parameters",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
_, _, err := client.GetActivities(context.Background(), 0, 0)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "invalid pagination parameters")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivitiesTimeout",
|
|
||||||
description: "Test request timeout handling",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
time.Sleep(2 * time.Second) // Simulate delay
|
|
||||||
})
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
_, _, err := client.GetActivities(ctx, 1, 10)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "context deadline exceeded")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivitiesInvalidResponse",
|
|
||||||
description: "Test invalid response handling",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte("invalid json"))
|
|
||||||
})
|
|
||||||
_, _, err := client.GetActivities(context.Background(), 1, 10)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "failed to parse response")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivitiesLargeDataset",
|
|
||||||
description: "Test handling of large activity datasets",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
// Create large dataset
|
|
||||||
var activities []ActivityResponse
|
|
||||||
for i := 0; i < 500; i++ {
|
|
||||||
activities = append(activities, ActivityResponse{
|
|
||||||
ActivityID: int64(i + 1),
|
|
||||||
Name: fmt.Sprintf("Activity %d", i+1),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
json.NewEncoder(w).Encode(ActivitiesResponse{
|
|
||||||
Activities: activities,
|
|
||||||
Pagination: Pagination{
|
|
||||||
Page: 1,
|
|
||||||
PageSize: 500,
|
|
||||||
TotalCount: 500,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
result, pagination, err := client.GetActivities(context.Background(), 1, 500)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, result, 500)
|
|
||||||
assert.Equal(t, 500, pagination.TotalCount)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivityDetailsInvalidResponse",
|
|
||||||
description: "Test invalid activity details response",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte("invalid json"))
|
|
||||||
})
|
|
||||||
_, err := client.GetActivityDetails(context.Background(), 1)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "failed to parse response")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "GetActivityDetailsMalformedID",
|
|
||||||
description: "Test handling of malformed activity ID in server response",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(`{"activityId": "invalid"}`)) // Should be number
|
|
||||||
})
|
|
||||||
_, err := client.GetActivityDetails(context.Background(), 1)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "failed to parse response")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "UploadActivitySuccess",
|
|
||||||
description: "Test successful activity upload",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
id, err := client.UploadActivity(context.Background(), []byte("test fit data"))
|
id, err := client.UploadActivity(context.Background(), []byte("test fit data"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(12345), id)
|
assert.Equal(t, int64(12345), id)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "UploadActivityInvalidData",
|
|
||||||
description: "Test uploading invalid FIT data",
|
|
||||||
testFunc: func(t *testing.T, client *Client) {
|
|
||||||
mockServer.SetUploadHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write([]byte(`{"error": "Invalid FIT file"}`))
|
|
||||||
})
|
|
||||||
_, err := client.UploadActivity(context.Background(), []byte("invalid"))
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "upload failed with status 400")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run test cases
|
for _, tt := range tests {
|
||||||
for _, tc := range testCases {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
mockServer.Reset()
|
||||||
t.Log(tc.description)
|
if tt.setup != nil {
|
||||||
tc.testFunc(t, client)
|
tt.setup()
|
||||||
|
}
|
||||||
|
tt.testFunc(t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -19,23 +20,24 @@ func BenchmarkGetSleepData(b *testing.B) {
|
|||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
|
||||||
// Setup successful response
|
// Setup handler for health endpoint
|
||||||
mockResponse := map[string]interface{}{
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
"date": testDate,
|
w.WriteHeader(http.StatusOK)
|
||||||
"duration": 480.0,
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
"quality": 85.0,
|
"date": testDate,
|
||||||
"sleepStages": map[string]interface{}{
|
"duration": 480.0,
|
||||||
"deep": 120.0,
|
"quality": 85.0,
|
||||||
"light": 240.0,
|
"sleepStages": map[string]interface{}{
|
||||||
"rem": 90.0,
|
"deep": 120.0,
|
||||||
"awake": 30.0,
|
"light": 240.0,
|
||||||
},
|
"rem": 90.0,
|
||||||
}
|
"awake": 30.0,
|
||||||
path := fmt.Sprintf("/wellness-service/sleep/daily/%s", now.Format("2006-01-02"))
|
},
|
||||||
mockServer.SetResponse(path, http.StatusOK, mockResponse)
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -52,18 +54,19 @@ func BenchmarkGetHRVData(b *testing.B) {
|
|||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
|
||||||
// Setup successful response
|
// Setup handler for health endpoint
|
||||||
mockResponse := map[string]interface{}{
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
"date": testDate,
|
w.WriteHeader(http.StatusOK)
|
||||||
"restingHrv": 65.0,
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
"weeklyAvg": 62.0,
|
"date": testDate,
|
||||||
"lastNightAvg": 68.0,
|
"restingHrv": 65.0,
|
||||||
}
|
"weeklyAvg": 62.0,
|
||||||
path := fmt.Sprintf("/hrv-service/hrv/%s", now.Format("2006-01-02"))
|
"lastNightAvg": 68.0,
|
||||||
mockServer.SetResponse(path, http.StatusOK, mockResponse)
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -80,19 +83,20 @@ func BenchmarkGetBodyBatteryData(b *testing.B) {
|
|||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
|
||||||
// Setup successful response
|
// Setup handler for health endpoint
|
||||||
mockResponse := map[string]interface{}{
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
"date": testDate,
|
w.WriteHeader(http.StatusOK)
|
||||||
"charged": 85,
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
"drained": 45,
|
"date": testDate,
|
||||||
"highest": 95,
|
"charged": 85,
|
||||||
"lowest": 30,
|
"drained": 45,
|
||||||
}
|
"highest": 95,
|
||||||
path := fmt.Sprintf("/bodybattery-service/bodybattery/%s", now.Format("2006-01-02"))
|
"lowest": 30,
|
||||||
mockServer.SetResponse(path, http.StatusOK, mockResponse)
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -166,13 +170,20 @@ func TestGetSleepData(t *testing.T) {
|
|||||||
|
|
||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
mockServer.Reset()
|
mockServer.Reset()
|
||||||
path := fmt.Sprintf("/wellness-service/sleep/daily/%s", tt.date.Format("2006-01-02"))
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
mockServer.SetResponse(path, tt.mockStatus, tt.mockResponse)
|
// Only handle sleep data requests
|
||||||
|
if strings.Contains(r.URL.Path, "sleep/daily") {
|
||||||
|
w.WriteHeader(tt.mockStatus)
|
||||||
|
json.NewEncoder(w).Encode(tt.mockResponse)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
data, err := client.GetSleepData(context.Background(), tt.date)
|
data, err := client.GetSleepData(context.Background(), tt.date)
|
||||||
|
|
||||||
@@ -230,13 +241,20 @@ func TestGetHRVData(t *testing.T) {
|
|||||||
|
|
||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
mockServer.Reset()
|
mockServer.Reset()
|
||||||
path := fmt.Sprintf("/hrv-service/hrv/%s", tt.date.Format("2006-01-02"))
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
mockServer.SetResponse(path, tt.mockStatus, tt.mockResponse)
|
// Only handle HRV data requests
|
||||||
|
if strings.Contains(r.URL.Path, "hrv/") {
|
||||||
|
w.WriteHeader(tt.mockStatus)
|
||||||
|
json.NewEncoder(w).Encode(tt.mockResponse)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
data, err := client.GetHRVData(context.Background(), tt.date)
|
data, err := client.GetHRVData(context.Background(), tt.date)
|
||||||
|
|
||||||
@@ -296,13 +314,20 @@ func TestGetBodyBatteryData(t *testing.T) {
|
|||||||
|
|
||||||
mockServer := NewMockServer()
|
mockServer := NewMockServer()
|
||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
mockServer.Reset()
|
mockServer.Reset()
|
||||||
path := fmt.Sprintf("/bodybattery-service/bodybattery/%s", tt.date.Format("2006-01-02"))
|
mockServer.SetHealthHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
mockServer.SetResponse(path, tt.mockStatus, tt.mockResponse)
|
// Only handle body battery requests
|
||||||
|
if strings.Contains(r.URL.Path, "bodybattery/") {
|
||||||
|
w.WriteHeader(tt.mockStatus)
|
||||||
|
json.NewEncoder(w).Encode(tt.mockResponse)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
data, err := client.GetBodyBatteryData(context.Background(), tt.date)
|
data, err := client.GetBodyBatteryData(context.Background(), tt.date)
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sstent/go-garminconnect/internal/auth/garth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockServer simulates the Garmin Connect API
|
// MockServer simulates the Garmin Connect API
|
||||||
type MockServer struct {
|
type MockServer struct {
|
||||||
server *httptest.Server
|
server *httptest.Server
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
// Endpoint handlers
|
// Endpoint handlers
|
||||||
activitiesHandler http.HandlerFunc
|
activitiesHandler http.HandlerFunc
|
||||||
activityDetailsHandler http.HandlerFunc
|
activityDetailsHandler http.HandlerFunc
|
||||||
@@ -21,31 +24,53 @@ type MockServer struct {
|
|||||||
userHandler http.HandlerFunc
|
userHandler http.HandlerFunc
|
||||||
healthHandler http.HandlerFunc
|
healthHandler http.HandlerFunc
|
||||||
authHandler http.HandlerFunc
|
authHandler http.HandlerFunc
|
||||||
|
|
||||||
|
// Request counters
|
||||||
|
requestCounters map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockServer creates a new mock Garmin Connect server
|
// NewMockServer creates a new mock Garmin Connect server
|
||||||
func NewMockServer() *MockServer {
|
func NewMockServer() *MockServer {
|
||||||
m := &MockServer{}
|
m := &MockServer{
|
||||||
|
requestCounters: make(map[string]int),
|
||||||
|
}
|
||||||
m.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
m.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
// Track request count
|
||||||
|
if m.requestCounters == nil {
|
||||||
|
m.requestCounters = make(map[string]int)
|
||||||
|
}
|
||||||
|
endpointType := "unknown"
|
||||||
|
path := r.URL.Path
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(r.URL.Path, "/activity-service/activities"):
|
case strings.HasPrefix(path, "/activitylist-service/activities/search") || path == "/activitylist-service/activities":
|
||||||
|
endpointType = "activities"
|
||||||
m.handleActivities(w, r)
|
m.handleActivities(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/activity-service/activity/"):
|
case strings.HasPrefix(path, "/activity-service/activities") || path == "/activity-service/activities":
|
||||||
|
endpointType = "activities"
|
||||||
|
m.handleActivities(w, r)
|
||||||
|
case strings.HasPrefix(path, "/activity-service/activity/"):
|
||||||
|
endpointType = "activityDetails"
|
||||||
m.handleActivityDetails(w, r)
|
m.handleActivityDetails(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/upload-service/upload"):
|
case strings.HasPrefix(path, "/upload-service/upload") || path == "/upload-service/upload":
|
||||||
|
endpointType = "upload"
|
||||||
m.handleUpload(w, r)
|
m.handleUpload(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/user-service/user"):
|
case strings.HasPrefix(path, "/user-service/user") || path == "/user-service/user":
|
||||||
|
endpointType = "user"
|
||||||
m.handleUserData(w, r)
|
m.handleUserData(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/health-service"):
|
case strings.HasPrefix(path, "/health-service") || path == "/health-service":
|
||||||
|
endpointType = "health"
|
||||||
m.handleHealthData(w, r)
|
m.handleHealthData(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/auth"):
|
case strings.HasPrefix(path, "/auth") || path == "/auth":
|
||||||
|
endpointType = "auth"
|
||||||
m.handleAuth(w, r)
|
m.handleAuth(w, r)
|
||||||
default:
|
default:
|
||||||
|
endpointType = "unknown"
|
||||||
http.Error(w, "Not found", http.StatusNotFound)
|
http.Error(w, "Not found", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
m.requestCounters[endpointType]++
|
||||||
}))
|
}))
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
@@ -74,6 +99,82 @@ func (m *MockServer) SetUploadHandler(handler http.HandlerFunc) {
|
|||||||
m.uploadHandler = handler
|
m.uploadHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetActivityDetailsHandler sets a custom handler for activity details endpoint
|
||||||
|
func (m *MockServer) SetActivityDetailsHandler(handler http.HandlerFunc) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.activityDetailsHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserHandler sets a custom handler for user endpoint
|
||||||
|
func (m *MockServer) SetUserHandler(handler http.HandlerFunc) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.userHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHealthHandler sets a custom handler for health endpoint
|
||||||
|
func (m *MockServer) SetHealthHandler(handler http.HandlerFunc) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.healthHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthHandler sets a custom handler for auth endpoint
|
||||||
|
func (m *MockServer) SetAuthHandler(handler http.HandlerFunc) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.authHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets all handlers and counters to default state
|
||||||
|
func (m *MockServer) Reset() {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.activitiesHandler = nil
|
||||||
|
m.activityDetailsHandler = nil
|
||||||
|
m.uploadHandler = nil
|
||||||
|
m.userHandler = nil
|
||||||
|
m.healthHandler = nil
|
||||||
|
m.authHandler = nil
|
||||||
|
m.requestCounters = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestCount returns the number of requests made to a specific endpoint
|
||||||
|
func (m *MockServer) RequestCount(endpoint string) int {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
return m.requestCounters[endpoint]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetResponse sets a standardized response for a specific endpoint
|
||||||
|
func (m *MockServer) SetResponse(endpoint string, status int, body interface{}) {
|
||||||
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
json.NewEncoder(w).Encode(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch endpoint {
|
||||||
|
case "activities":
|
||||||
|
m.SetActivitiesHandler(handler)
|
||||||
|
case "activityDetails":
|
||||||
|
m.SetActivityDetailsHandler(handler)
|
||||||
|
case "upload":
|
||||||
|
m.SetUploadHandler(handler)
|
||||||
|
case "user":
|
||||||
|
m.SetUserHandler(handler)
|
||||||
|
case "health":
|
||||||
|
m.SetHealthHandler(handler)
|
||||||
|
case "auth":
|
||||||
|
m.SetAuthHandler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetErrorResponse configures an error response for a specific endpoint
|
||||||
|
func (m *MockServer) SetErrorResponse(endpoint string, status int, message string) {
|
||||||
|
m.SetResponse(endpoint, status, map[string]string{"error": message})
|
||||||
|
}
|
||||||
|
|
||||||
// Default handler implementations would follow for each endpoint
|
// Default handler implementations would follow for each endpoint
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
@@ -187,10 +288,39 @@ func (m *MockServer) handleAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
m.authHandler(w, r)
|
m.authHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle session refresh requests
|
||||||
|
if strings.Contains(r.URL.Path, "/refresh") {
|
||||||
|
// Validate refresh token and return new access token
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"oauth2_token": "new-mock-token",
|
||||||
|
"expires_in": 3600,
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate successful authentication
|
// Simulate successful authentication
|
||||||
response := map[string]interface{}{
|
response := map[string]interface{}{
|
||||||
"token": "mock-token-123",
|
"oauth2_token": "mock-access-token",
|
||||||
|
"refresh_token": "mock-refresh-token",
|
||||||
|
"expires_in": 3600,
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewClientWithBaseURL creates a test client that uses the mock server's URL
|
||||||
|
func NewClientWithBaseURL(baseURL string) *Client {
|
||||||
|
session := &garth.Session{
|
||||||
|
OAuth2Token: "mock-token",
|
||||||
|
ExpiresAt: time.Now().Add(8 * time.Hour),
|
||||||
|
}
|
||||||
|
client, err := NewClient(session, "")
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to create test client: " + err.Error())
|
||||||
|
}
|
||||||
|
client.HTTPClient.SetBaseURL(baseURL)
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func TestGetUserProfile(t *testing.T) {
|
|||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -127,7 +127,7 @@ func BenchmarkGetUserProfile(b *testing.B) {
|
|||||||
mockServer.SetResponse("/userprofile-service/socialProfile", http.StatusOK, mockResponse)
|
mockServer.SetResponse("/userprofile-service/socialProfile", http.StatusOK, mockResponse)
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -157,7 +157,7 @@ func BenchmarkGetUserStats(b *testing.B) {
|
|||||||
mockServer.SetResponse(path, http.StatusOK, mockResponse)
|
mockServer.SetResponse(path, http.StatusOK, mockResponse)
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -233,7 +233,7 @@ func TestGetUserStats(t *testing.T) {
|
|||||||
defer mockServer.Close()
|
defer mockServer.Close()
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client := NewClientWithBaseURL(mockServer.URL)
|
client := NewClientWithBaseURL(mockServer.URL())
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user