diff --git a/Makefile b/Makefile index be13d1d0..88919921 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,10 @@ build-templates: templ generate test-templates: - go test ./views/*/ + go test ./views/... + +test-commands: + go test ./cmd/... format-templates: find . -type f -name '*.templ' -exec templ fmt -v {} \; @@ -96,7 +99,7 @@ format-templates: serve: $(WT_OUTPUT_FILE) -test: test-go test-assets test-templates +test: test-go test-assets test-templates test-commands test-assets: prettier --check . diff --git a/cmd/wt-debug/workouts.go b/cmd/wt-debug/workouts.go index e6cb6355..386f6231 100644 --- a/cmd/wt-debug/workouts.go +++ b/cmd/wt-debug/workouts.go @@ -31,7 +31,7 @@ func (c *cli) workoutsDiagCmd() *cobra.Command { t := table.New(os.Stdout) t.SetHeaders("ID", "Name", "Issues") - var ids []uint + var ids []uint64 if err := c.getDatabase().Model(&database.Workout{}).Pluck("ID", &ids).Error; err != nil { return err @@ -40,7 +40,7 @@ func (c *cli) workoutsDiagCmd() *cobra.Command { for _, id := range ids { issues := []string{} - wo, err := database.GetWorkout(c.getDatabase(), int(id)) + wo, err := database.GetWorkout(c.getDatabase(), id) if err != nil { issues = append(issues, err.Error()) } @@ -49,7 +49,7 @@ func (c *cli) workoutsDiagCmd() *cobra.Command { issues = []string{"OK"} } - t.AddRow(strconv.FormatUint(uint64(id), 10), wo.Name, strings.Join(issues, "; ")) + t.AddRow(strconv.FormatUint(id, 10), wo.Name, strings.Join(issues, "; ")) } t.Render() @@ -73,7 +73,7 @@ func (c *cli) workoutsListCmd() *cobra.Command { } for _, wo := range workouts { - t.AddRow(strconv.FormatUint(uint64(wo.ID), 10), wo.Date.String(), wo.Name) + t.AddRow(strconv.FormatUint(wo.ID, 10), wo.Date.String(), wo.Name) } t.Render() @@ -89,7 +89,7 @@ func (c *cli) workoutsShowCmd() *cobra.Command { Short: "Show information about a workout", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - id, err := strconv.Atoi(args[0]) + id, err := strconv.ParseUint(args[0], 10, 32) if err != nil { return err } @@ -102,7 +102,7 @@ func (c *cli) workoutsShowCmd() *cobra.Command { return err } - t.AddRow("ID", strconv.FormatUint(uint64(wo.ID), 10)) + t.AddRow("ID", strconv.FormatUint(wo.ID, 10)) t.AddRow("Date", wo.Date.String()) t.AddRow("Name", wo.Name) diff --git a/pkg/app/background.go b/pkg/app/background.go index b55bf59d..e7ba0cea 100644 --- a/pkg/app/background.go +++ b/pkg/app/background.go @@ -245,7 +245,7 @@ func (a *App) updateRouteSegments(l *slog.Logger) { } func (a *App) updateWorkout(l *slog.Logger) { - var wID []int + var wID []uint64 db := a.db.Preload("Data.Details").Preload("User") @@ -263,7 +263,7 @@ func (a *App) updateWorkout(l *slog.Logger) { } } -func (a *App) UpdateWorkout(i int) error { +func (a *App) UpdateWorkout(i uint64) error { w, err := database.GetWorkoutWithGPX(a.db, i) if err != nil { return err diff --git a/pkg/app/workouts.go b/pkg/app/workouts.go index 91d7540d..c55964d6 100644 --- a/pkg/app/workouts.go +++ b/pkg/app/workouts.go @@ -167,7 +167,7 @@ func (a *App) addWorkout(c echo.Context) error { workout.Data.Creator = "web-interface" var equipmentIDS struct { - EquipmentIDs []uint `form:"equipment"` + EquipmentIDs []uint64 `form:"equipment"` } if err := c.Bind(&equipmentIDS); err != nil { @@ -206,7 +206,7 @@ func (a *App) workoutsUpdateHandler(c echo.Context) error { d.Update(workout) var equipmentIDS struct { - EquipmentIDs []uint `form:"equipment"` + EquipmentIDs []uint64 `form:"equipment"` } if err := c.Bind(&equipmentIDS); err != nil { diff --git a/pkg/app/workouts_handlers.go b/pkg/app/workouts_handlers.go index 34ac1cbb..25e895f4 100644 --- a/pkg/app/workouts_handlers.go +++ b/pkg/app/workouts_handlers.go @@ -55,7 +55,7 @@ func (a *App) workoutsHandler(c echo.Context) error { func (a *App) workoutsShowHandler(c echo.Context) error { a.setContext(c) - id, err := strconv.Atoi(c.Param("id")) + id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return a.redirectWithError(c, a.echo.Reverse("workouts"), err) } @@ -210,7 +210,7 @@ func (a *App) workoutsEditHandler(c echo.Context) error { } func (a *App) workoutsCreateRouteSegmentFromWorkoutHandler(c echo.Context) error { - id, err := strconv.Atoi(c.Param("id")) + id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return a.redirectWithError(c, a.echo.Reverse("workouts"), err) } @@ -244,7 +244,7 @@ func (a *App) workoutsCreateRouteSegmentFromWorkoutHandler(c echo.Context) error func (a *App) workoutsCreateRouteSegmentHandler(c echo.Context) error { a.setContext(c) - id, err := strconv.Atoi(c.Param("id")) + id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { return a.redirectWithError(c, a.echo.Reverse("workouts"), err) } diff --git a/pkg/database/equipment.go b/pkg/database/equipment.go index ee44aba1..1afecaf7 100644 --- a/pkg/database/equipment.go +++ b/pkg/database/equipment.go @@ -20,16 +20,16 @@ type Equipment struct { User User - UserID uint `gorm:"not null;index"` // The ID of the user who owns the workout - Active bool `gorm:"default:true" json:"active" form:"active"` // Whether this equipment is active + UserID uint64 `gorm:"not null;index"` // The ID of the user who owns the workout + Active bool `gorm:"default:true" json:"active" form:"active"` // Whether this equipment is active } type WorkoutEquipment struct { Model Workout Workout Equipment Equipment - WorkoutID uint `gorm:"not null;uniqueIndex:idx_workout_equipment"` // The ID of the workout - EquipmentID uint `gorm:"not null;uniqueIndex:idx_workout_equipment"` // The ID of the equipment + WorkoutID uint64 `gorm:"not null;uniqueIndex:idx_workout_equipment"` // The ID of the workout + EquipmentID uint64 `gorm:"not null;uniqueIndex:idx_workout_equipment"` // The ID of the equipment } func GetEquipment(db *gorm.DB, id int) (*Equipment, error) { @@ -60,7 +60,7 @@ func (e *Equipment) Save(db *gorm.DB) error { return db.Omit(clause.Associations).Save(e).Error } -func GetEquipmentByIDs(db *gorm.DB, userID uint, ids []uint) ([]*Equipment, error) { +func GetEquipmentByIDs(db *gorm.DB, userID uint64, ids []uint64) ([]*Equipment, error) { var equipment []*Equipment if len(ids) == 0 { diff --git a/pkg/database/gorm.go b/pkg/database/gorm.go index e2f0e80e..9d2acac5 100644 --- a/pkg/database/gorm.go +++ b/pkg/database/gorm.go @@ -22,7 +22,7 @@ var ErrUnsuportedDriver = errors.New("unsupported driver") type Model struct { CreatedAt time.Time UpdatedAt time.Time - ID uint `gorm:"primaryKey"` + ID uint64 `gorm:"primaryKey"` } func Connect(driver, dsn string, debug bool, logger *slog.Logger) (*gorm.DB, error) { diff --git a/pkg/database/profile.go b/pkg/database/profile.go index f83db0f2..38fe5fb3 100644 --- a/pkg/database/profile.go +++ b/pkg/database/profile.go @@ -20,7 +20,7 @@ type Profile struct { TotalsShow WorkoutType `form:"totals_show"` // What workout type of totals to show Timezone string `form:"timezone"` // The user's preferred timezone AutoImportDirectory string `form:"auto_import_directory"` // The user's preferred directory for auto-import - UserID uint // The ID of the user who owns this profile + UserID uint64 // The ID of the user who owns this profile APIActive bool `form:"api_active"` // Whether the user's API key is active SocialsDisabled bool `form:"socials_disabled"` // Whether social sharing buttons are disabled when viewing a workout PreferFullDate bool `form:"prefer_full_date"` // Whether to show full dates in the workout details diff --git a/pkg/database/route_segment_matching.go b/pkg/database/route_segment_matching.go index 3ff24247..b27e78eb 100644 --- a/pkg/database/route_segment_matching.go +++ b/pkg/database/route_segment_matching.go @@ -22,8 +22,8 @@ type RouteSegmentMatch struct { first, last MapPoint // The first and last point of the route end MapPoint // The last point of the workout - RouteSegmentID uint `gorm:"primaryKey"` // The ID of the route segment - WorkoutID uint `gorm:"primaryKey"` // The ID of the workout + RouteSegmentID uint64 `gorm:"primaryKey"` // The ID of the route segment + WorkoutID uint64 `gorm:"primaryKey"` // The ID of the workout FirstID, LastID int // The index of the first and last point of the route Distance float64 // The total distance of the route segment for this workout Duration time.Duration // The total duration of the route segment for this workout diff --git a/pkg/database/user_statistics.go b/pkg/database/user_statistics.go index c761f676..5fb96ce1 100644 --- a/pkg/database/user_statistics.go +++ b/pkg/database/user_statistics.go @@ -11,7 +11,7 @@ type ( Statistics struct { Buckets map[WorkoutType]Buckets // The statistics buckets BucketFormat string // The bucket format in strftime format - UserID uint // The user ID + UserID uint64 // The user ID } Buckets struct { @@ -46,14 +46,14 @@ type ( Float64Record struct { Date time.Time // The timestamp of the record Value float64 // The value of the record - ID uint // The workout ID of the record + ID uint64 // The workout ID of the record } // DurationRecord is a single record if the value is a time.Duration DurationRecord struct { Date time.Time // The timestamp of the record Value time.Duration // The value of the record - ID uint // The workout ID of the record + ID uint64 // The workout ID of the record } // WorkoutRecord is the collection of records for a single workout type diff --git a/pkg/database/workouts.go b/pkg/database/workouts.go index edab4384..f7bfe023 100644 --- a/pkg/database/workouts.go +++ b/pkg/database/workouts.go @@ -34,7 +34,7 @@ type Workout struct { Type WorkoutType // The type of the workout Equipment []Equipment `json:",omitempty" gorm:"constraint:OnDelete:CASCADE;many2many:workout_equipment"` // Which equipment is used for this workout RouteSegmentMatches []*RouteSegmentMatch `gorm:"constraint:OnDelete:CASCADE" json:",omitempty"` // Which route segments match - UserID uint `gorm:"not null;index;uniqueIndex:idx_start_user"` // The ID of the user who owns the workout + UserID uint64 `gorm:"not null;index;uniqueIndex:idx_start_user"` // The ID of the user who owns the workout Dirty bool // Whether the workout has been modified and the details should be re-rendered } @@ -43,7 +43,7 @@ type GPXData struct { Filename string // The filename of the file Content []byte `gorm:"type:text"` // The file content Checksum []byte `gorm:"not null;uniqueIndex"` // The checksum of the content - WorkoutID uint `gorm:"not null;uniqueIndex"` // The ID of the workout + WorkoutID uint64 `gorm:"not null;uniqueIndex"` // The ID of the workout } func (w *Workout) GetDate() time.Time { @@ -415,7 +415,7 @@ func GetWorkoutWithGPXByUUID(db *gorm.DB, u uuid.UUID) (*Workout, error) { return GetWorkoutByUUID(db.Preload("GPX"), u) } -func GetWorkoutWithGPX(db *gorm.DB, id int) (*Workout, error) { +func GetWorkoutWithGPX(db *gorm.DB, id uint64) (*Workout, error) { return GetWorkout(db.Preload("GPX").Preload("Data.Details"), id) } @@ -423,7 +423,7 @@ func GetWorkoutDetailsByUUID(db *gorm.DB, u uuid.UUID) (*Workout, error) { return GetWorkoutWithGPXByUUID(db.Preload("Data.Details"), u) } -func GetWorkoutDetails(db *gorm.DB, id int) (*Workout, error) { +func GetWorkoutDetails(db *gorm.DB, id uint64) (*Workout, error) { return GetWorkoutWithGPX(db.Preload("Data.Details"), id) } @@ -450,7 +450,7 @@ func GetWorkoutByUUID(db *gorm.DB, u uuid.UUID) (*Workout, error) { return &w, nil } -func GetWorkout(db *gorm.DB, id int) (*Workout, error) { +func GetWorkout(db *gorm.DB, id uint64) (*Workout, error) { var w Workout if err := db. @@ -655,8 +655,8 @@ func (w *Workout) HasExtraMetric(name string) bool { return slices.Contains(w.Data.ExtraMetrics, name) } -func (w *Workout) EquipmentIDs() []uint { - ids := make([]uint, 0, len(w.Equipment)) +func (w *Workout) EquipmentIDs() []uint64 { + ids := make([]uint64, 0, len(w.Equipment)) for _, e := range w.Equipment { ids = append(ids, e.ID) diff --git a/pkg/database/workouts_map.go b/pkg/database/workouts_map.go index e48a9b6e..7784be20 100644 --- a/pkg/database/workouts_map.go +++ b/pkg/database/workouts_map.go @@ -67,7 +67,7 @@ type MapData struct { Name string // The name of the workout AddressString string // The generic location of the workout Center MapCenter `gorm:"serializer:json"` // The center of the workout (in coordinates) - WorkoutID uint `gorm:"not null;uniqueIndex"` // The workout this data belongs to + WorkoutID uint64 `gorm:"not null;uniqueIndex"` // The workout this data belongs to TotalDistance float64 // The total distance of the workout TotalDuration time.Duration // The total duration of the workout MaxSpeed float64 // The maximum speed of the workout @@ -89,7 +89,7 @@ type MapDataDetails struct { MapData *MapData `gorm:"foreignKey:MapDataID" json:"-"` Points []MapPoint `gorm:"serializer:json"` // The GPS points of the workout - MapDataID uint `gorm:"not null;uniqueIndex"` // The ID of the map data these details belong to + MapDataID uint64 `gorm:"not null;uniqueIndex"` // The ID of the map data these details belong to } // MapCenter is the center of the workout diff --git a/pkg/database/workouts_test.go b/pkg/database/workouts_test.go index d94e8e17..ffb31386 100644 --- a/pkg/database/workouts_test.go +++ b/pkg/database/workouts_test.go @@ -99,7 +99,7 @@ func TestWorkout_SaveAndGet(t *testing.T) { require.NoError(t, w.Save(db)) assert.NotZero(t, w.UpdatedAt) - newW, err := GetWorkoutDetails(db, int(w.ID)) + newW, err := GetWorkoutDetails(db, w.ID) require.NoError(t, err) assert.Equal(t, w.ID, newW.ID) assert.Equal(t, w.Data.Details.Points, newW.Data.Details.Points) diff --git a/views/helpers/helpers.go b/views/helpers/helpers.go index d36da0af..575beec3 100644 --- a/views/helpers/helpers.go +++ b/views/helpers/helpers.go @@ -90,7 +90,7 @@ func A2S(v any) string { } return "false" - case int, uint: + case int, uint64: return fmt.Sprintf("%d", e) case float64: return fmt.Sprintf("%.2f", e) diff --git a/views/user/stats.templ b/views/user/stats.templ index 770dec88..c1bdf6ac 100644 --- a/views/user/stats.templ +++ b/views/user/stats.templ @@ -49,7 +49,7 @@ templ StatsRecordDistanceDate(workout *database.Float64Record) { @StatsRecordDate(workout.ID, &workout.Date) } -templ StatsRecordDate(id uint, date *time.Time) { +templ StatsRecordDate(id uint64, date *time.Time) {