Some checks failed
Deploy to NAS / deploy (push) Failing after 14s
- Introduce store interfaces (TrackpointStorer, StopStorer, SuggestionStorer) in internal/db/interfaces.go so handlers can be tested without a real DB. - Refactor HandleSingleTrackpoint, HandleBatchTrackpoints, HandleListDays, HandleListTrackpoints, HandleListStops, HandleListSuggestions to accept the new interfaces instead of concrete *db.*Store pointers (no behaviour change; concrete types satisfy the interfaces implicitly). - internal/api/ingest_test.go: 13 handler tests covering happy path, invalid JSON, invalid timestamp, missing event_id/device_id, out-of-range lat/lon, empty/oversized batch, store errors, and idempotency (single + batch). - internal/api/query_test.go: 14 handler tests covering missing query params (400) and empty-result-is-array guarantees for all four query endpoints. - internal/auth/auth_test.go: 5 unit tests for HashPassword / VerifyPassword (correct password, wrong password, empty password, malformed hash, salt uniqueness). - internal/db/trackpoints_test.go: 6 unit tests for the validateTrackpoint helper (happy path, missing fields, coordinate bounds, invalid source). - .gitea/workflows/deploy.yml: add "Test" step (go test ./...) before "Build & Deploy" so a failing test aborts the deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
3.1 KiB
Go
103 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/jacek/pamietnik/backend/internal/db"
|
|
"github.com/jacek/pamietnik/backend/internal/domain"
|
|
)
|
|
|
|
// HandleListDays handles GET /v1/days?from=YYYY-MM-DD&to=YYYY-MM-DD
|
|
func HandleListDays(store db.TrackpointStorer) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID := userIDFromContext(r.Context())
|
|
from := r.URL.Query().Get("from")
|
|
to := r.URL.Query().Get("to")
|
|
if from == "" || to == "" {
|
|
writeError(w, http.StatusBadRequest, "BAD_REQUEST", "from and to are required (YYYY-MM-DD)")
|
|
return
|
|
}
|
|
|
|
days, err := store.ListDays(r.Context(), userID, from, to)
|
|
if err != nil {
|
|
slog.Error("list days", "user_id", userID, "err", err)
|
|
writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "database error")
|
|
return
|
|
}
|
|
if days == nil {
|
|
days = []domain.DaySummary{}
|
|
}
|
|
writeJSON(w, http.StatusOK, days)
|
|
}
|
|
}
|
|
|
|
// HandleListTrackpoints handles GET /v1/trackpoints?date=YYYY-MM-DD
|
|
func HandleListTrackpoints(store db.TrackpointStorer) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID := userIDFromContext(r.Context())
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
writeError(w, http.StatusBadRequest, "BAD_REQUEST", "date is required (YYYY-MM-DD)")
|
|
return
|
|
}
|
|
|
|
points, err := store.ListByDate(r.Context(), userID, date)
|
|
if err != nil {
|
|
slog.Error("list trackpoints", "user_id", userID, "date", date, "err", err)
|
|
writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "database error")
|
|
return
|
|
}
|
|
if points == nil {
|
|
points = []domain.Trackpoint{}
|
|
}
|
|
writeJSON(w, http.StatusOK, points)
|
|
}
|
|
}
|
|
|
|
// HandleListStops handles GET /v1/stops?date=YYYY-MM-DD
|
|
func HandleListStops(store db.StopStorer) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID := userIDFromContext(r.Context())
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
writeError(w, http.StatusBadRequest, "BAD_REQUEST", "date is required (YYYY-MM-DD)")
|
|
return
|
|
}
|
|
|
|
stops, err := store.ListByDate(r.Context(), userID, date)
|
|
if err != nil {
|
|
slog.Error("list stops", "user_id", userID, "date", date, "err", err)
|
|
writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "database error")
|
|
return
|
|
}
|
|
if stops == nil {
|
|
stops = []domain.Stop{}
|
|
}
|
|
writeJSON(w, http.StatusOK, stops)
|
|
}
|
|
}
|
|
|
|
// HandleListSuggestions handles GET /v1/suggestions?date=YYYY-MM-DD
|
|
func HandleListSuggestions(store db.SuggestionStorer) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID := userIDFromContext(r.Context())
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
writeError(w, http.StatusBadRequest, "BAD_REQUEST", "date is required (YYYY-MM-DD)")
|
|
return
|
|
}
|
|
|
|
suggestions, err := store.ListByDate(r.Context(), userID, date)
|
|
if err != nil {
|
|
slog.Error("list suggestions", "user_id", userID, "date", date, "err", err)
|
|
writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "database error")
|
|
return
|
|
}
|
|
if suggestions == nil {
|
|
suggestions = []domain.Suggestion{}
|
|
}
|
|
writeJSON(w, http.StatusOK, suggestions)
|
|
}
|
|
}
|