Server no longer holds one fixed Garmin/Sync pair -- garminFor/syncFor build and cache one instance per user, keyed off their own profile's Garmin credentials and a per-user token-store subdirectory. The background incremental sync loop now iterates every provisioned user each tick instead of syncing one global account.
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
authmock "geniusrun/backend/internal/auth/mock"
|
|
"geniusrun/backend/internal/garmin"
|
|
"geniusrun/backend/internal/garmin/mock"
|
|
"geniusrun/backend/internal/store"
|
|
appsync "geniusrun/backend/internal/sync"
|
|
)
|
|
|
|
func TestSetup_ProvisionsNewUserWithDisplayName(t *testing.T) {
|
|
// This test specifically needs an *unprovisioned* session, unlike every
|
|
// other test in this package -- build the server without the
|
|
// newTestServer helper's automatic ProvisionUser call.
|
|
db, err := store.Open(filepath.Join(t.TempDir(), "geniusrun_test.db"))
|
|
if err != nil {
|
|
t.Fatalf("store.Open: %v", err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
m := &mock.Client{}
|
|
s := NewServer(db, func(garmin.Config) garmin.Client { return m }, garmin.Config{}, appsync.Config{}, &authmock.Verifier{}, testSessionConfig)
|
|
router := s.Router()
|
|
|
|
rec := doJSON(t, router, http.MethodGet, "/api/session/me", nil)
|
|
var me sessionMeResponse
|
|
unmarshalBody(t, rec, &me)
|
|
if me.HasProfile {
|
|
t.Fatal("expected a brand-new session to have no profile yet")
|
|
}
|
|
|
|
rec = doJSON(t, router, http.MethodPost, "/api/setup", map[string]any{"display_name": "Lucie"})
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("setup status = %d, body = %s", rec.Code, rec.Body.String())
|
|
}
|
|
|
|
rec = doJSON(t, router, http.MethodGet, "/api/session/me", nil)
|
|
unmarshalBody(t, rec, &me)
|
|
if !me.HasProfile || me.DisplayName != "Lucie" {
|
|
t.Fatalf("session/me after setup = %+v, want HasProfile=true DisplayName=Lucie", me)
|
|
}
|
|
|
|
u, found, err := db.GetUserBySub(newCtx(), "test-user") // doJSON's test cookie's Sub
|
|
if err != nil || !found {
|
|
t.Fatalf("GetUserBySub: found=%v err=%v", found, err)
|
|
}
|
|
if u.DisplayName != "Lucie" {
|
|
t.Errorf("stored DisplayName = %q, want Lucie", u.DisplayName)
|
|
}
|
|
}
|
|
|
|
func TestSetup_RejectsEmptyDisplayName(t *testing.T) {
|
|
db, err := store.Open(filepath.Join(t.TempDir(), "geniusrun_test.db"))
|
|
if err != nil {
|
|
t.Fatalf("store.Open: %v", err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
m := &mock.Client{}
|
|
s := NewServer(db, func(garmin.Config) garmin.Client { return m }, garmin.Config{}, appsync.Config{}, &authmock.Verifier{}, testSessionConfig)
|
|
|
|
rec := doJSON(t, s.Router(), http.MethodPost, "/api/setup", map[string]any{"display_name": ""})
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status = %d, want 400", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestSetup_RejectsWhenAlreadyProvisioned(t *testing.T) {
|
|
s, _ := newTestServer(t)
|
|
rec := doJSON(t, s.Router(), http.MethodPost, "/api/setup", map[string]any{"display_name": "Someone Else"})
|
|
if rec.Code != http.StatusConflict {
|
|
t.Fatalf("status = %d, want 409, body = %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func unmarshalBody(t *testing.T, rec *httptest.ResponseRecorder, v any) {
|
|
t.Helper()
|
|
if err := json.Unmarshal(rec.Body.Bytes(), v); err != nil {
|
|
t.Fatalf("unmarshal response body %q: %v", rec.Body.String(), err)
|
|
}
|
|
}
|