Files
go-garminconnect/internal/api/activities_test.go
2025-08-27 11:58:01 -07:00

239 lines
7.4 KiB
Go

package api
import (
"context"
"fmt"
"net/http"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// TestGetActivities is now part of table-driven tests below
func TestActivitiesEndpoints(t *testing.T) {
// Create mock server
mockServer := NewMockServer()
defer mockServer.Close()
// Create client with mock server URL
client, err := NewClient(mockServer.URL(), nil)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
testCases := []struct {
name string
testFunc func(t *testing.T, client *Client)
description string
}{
{
name: "GetActivitiesSuccess",
description: "Test successful activity list retrieval",
testFunc: func(t *testing.T, client *Client) {
activities, pagination, err := client.GetActivities(context.Background(), 1, 10)
assert.NoError(t, err)
assert.Len(t, activities, 1)
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",
description: "Test successful activity details retrieval",
testFunc: func(t *testing.T, client *Client) {
activity, err := client.GetActivityDetails(context.Background(), 1)
assert.NoError(t, err)
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",
description: "Test server error handling for activity list",
testFunc: func(t *testing.T, client *Client) {
mockServer.SetActivitiesHandler(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
})
_, _, err := client.GetActivities(context.Background(), 1, 10)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to get activities")
},
},
{
name: "GetActivityDetailsNotFound",
description: "Test not found error for activity details",
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"))
assert.NoError(t, err)
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 _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Log(tc.description)
tc.testFunc(t, client)
})
}
}
func TestValidateFIT(t *testing.T) {
testCases := []struct {
name string
data []byte
expected error
}{
{
name: "ValidFIT",
data: []byte{0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, '.', 'F', 'I', 'T', 0x00, 0x00},
expected: nil,
},
{
name: "TooSmall",
data: []byte{0x0E},
expected: fmt.Errorf("file too small to be a valid FIT file"),
},
{
name: "InvalidSignature",
data: []byte{0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'I', 'N', 'V', 'L', 0x00, 0x00},
expected: fmt.Errorf("invalid FIT file signature"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateFIT(tc.data)
if tc.expected == nil {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expected.Error())
}
})
}
}