fix: read GOOGLE_SHEETS_ID and default range to current-year tab
LoadConfig read SPREADSHEET_ID, but the .env defines GOOGLE_SHEETS_ID, so config loading failed. The default SHEET_RANGE was also a hardcoded 'Dyżury!A1:F100', but the spreadsheet tabs are named per year (e.g. '2026'), producing a 400 'Unable to parse range'. Read GOOGLE_SHEETS_ID and derive the default range from the current year. Add a RUN_LIVE-gated integration test that drives the full LoadConfig -> Service -> Syncer.Sync -> Store path against the live Sheet, and gitignore the generated rota.json cache.
This commit is contained in:
parent
572701106c
commit
cb0cc679c8
|
|
@ -1 +1,2 @@
|
|||
.env
|
||||
rota.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package dyzurbot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestLiveFetch exercises the full production path — LoadConfig reads the
|
||||
// environment/.env, NewService wires the Store + Syncer, and Syncer.Sync hits
|
||||
// the live Google Sheets API, parses the result, and persists it to the cache
|
||||
// file via Store.Replace. The resulting JSON is left on disk (CachePath,
|
||||
// default rota.json) for inspection.
|
||||
//
|
||||
// It is skipped unless RUN_LIVE=1, so the normal `go test ./...` run stays
|
||||
// offline and fast.
|
||||
//
|
||||
// RUN_LIVE=1 go test -run TestLiveFetch -v .
|
||||
//
|
||||
// Requires GOOGLE_API_KEY and GOOGLE_SHEETS_ID in the environment/.env.
|
||||
func TestLiveFetch(t *testing.T) {
|
||||
if os.Getenv("RUN_LIVE") != "1" {
|
||||
t.Skip("set RUN_LIVE=1 to run the live Google Sheets fetch")
|
||||
}
|
||||
|
||||
cfg, err := LoadConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig: %v", err)
|
||||
}
|
||||
t.Logf("config: spreadsheet=%s range=%q cache=%s",
|
||||
cfg.SpreadsheetID, cfg.SheetRange, cfg.CachePath)
|
||||
|
||||
svc := NewService(cfg)
|
||||
if err := svc.Syncer.Sync(context.Background()); err != nil {
|
||||
t.Fatalf("sync: %v", err)
|
||||
}
|
||||
|
||||
rota, lastSynced := svc.Store.Snapshot()
|
||||
t.Logf("synced %d weeks, %d people at %s",
|
||||
len(rota.Weeks), len(rota.People), lastSynced.Format("2006-01-02 15:04:05"))
|
||||
|
||||
// Confirm the JSON actually landed on disk and show where.
|
||||
written, err := os.ReadFile(cfg.CachePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read cache %q: %v", cfg.CachePath, err)
|
||||
}
|
||||
abs, _ := filepath.Abs(cfg.CachePath)
|
||||
t.Logf("wrote cache JSON (%d bytes) to %s", len(written), abs)
|
||||
}
|
||||
10
service.go
10
service.go
|
|
@ -22,13 +22,17 @@ type Config struct {
|
|||
func LoadConfig() (Config, error) {
|
||||
_ = godotenv.Load()
|
||||
|
||||
// Default range targets the current year's tab (sheets are named per year,
|
||||
// e.g. "2026"), matching the spreadsheet layout.
|
||||
defaultRange := fmt.Sprintf("%d!A1:F100", time.Now().Year())
|
||||
|
||||
cfg := Config{
|
||||
SpreadsheetID: os.Getenv("SPREADSHEET_ID"),
|
||||
SheetRange: getenvDefault("SHEET_RANGE", "Dyżury!A1:F100"),
|
||||
SpreadsheetID: os.Getenv("GOOGLE_SHEETS_ID"),
|
||||
SheetRange: getenvDefault("SHEET_RANGE", defaultRange),
|
||||
CachePath: getenvDefault("ROTA_CACHE_PATH", "rota.json"),
|
||||
}
|
||||
if cfg.SpreadsheetID == "" {
|
||||
return Config{}, fmt.Errorf("missing SPREADSHEET_ID in environment/.env")
|
||||
return Config{}, fmt.Errorf("missing GOOGLE_SHEETS_ID in environment/.env")
|
||||
}
|
||||
|
||||
raw := getenvDefault("SYNC_INTERVAL", "30m")
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package dyzurbot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadConfigDefaults(t *testing.T) {
|
||||
t.Setenv("SPREADSHEET_ID", "sheet-123")
|
||||
t.Setenv("GOOGLE_SHEETS_ID", "sheet-123")
|
||||
t.Setenv("SHEET_RANGE", "")
|
||||
t.Setenv("ROTA_CACHE_PATH", "")
|
||||
t.Setenv("SYNC_INTERVAL", "")
|
||||
|
|
@ -18,8 +19,9 @@ func TestLoadConfigDefaults(t *testing.T) {
|
|||
if cfg.SpreadsheetID != "sheet-123" {
|
||||
t.Errorf("SpreadsheetID: got %q", cfg.SpreadsheetID)
|
||||
}
|
||||
if cfg.SheetRange != "Dyżury!A1:F100" {
|
||||
t.Errorf("SheetRange default: got %q", cfg.SheetRange)
|
||||
wantRange := fmt.Sprintf("%d!A1:F100", time.Now().Year())
|
||||
if cfg.SheetRange != wantRange {
|
||||
t.Errorf("SheetRange default: got %q, want %q", cfg.SheetRange, wantRange)
|
||||
}
|
||||
if cfg.CachePath != "rota.json" {
|
||||
t.Errorf("CachePath default: got %q", cfg.CachePath)
|
||||
|
|
@ -30,14 +32,14 @@ func TestLoadConfigDefaults(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoadConfigRequiresSpreadsheetID(t *testing.T) {
|
||||
t.Setenv("SPREADSHEET_ID", "")
|
||||
t.Setenv("GOOGLE_SHEETS_ID", "")
|
||||
if _, err := LoadConfig(); err == nil {
|
||||
t.Fatal("expected error when SPREADSHEET_ID is missing")
|
||||
t.Fatal("expected error when GOOGLE_SHEETS_ID is missing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRejectsBadInterval(t *testing.T) {
|
||||
t.Setenv("SPREADSHEET_ID", "sheet-123")
|
||||
t.Setenv("GOOGLE_SHEETS_ID", "sheet-123")
|
||||
t.Setenv("SYNC_INTERVAL", "not-a-duration")
|
||||
if _, err := LoadConfig(); err == nil {
|
||||
t.Fatal("expected error for unparseable SYNC_INTERVAL")
|
||||
|
|
@ -45,7 +47,7 @@ func TestLoadConfigRejectsBadInterval(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoadConfigRejectsNonPositiveInterval(t *testing.T) {
|
||||
t.Setenv("SPREADSHEET_ID", "sheet-123")
|
||||
t.Setenv("GOOGLE_SHEETS_ID", "sheet-123")
|
||||
t.Setenv("SYNC_INTERVAL", "0s")
|
||||
if _, err := LoadConfig(); err == nil {
|
||||
t.Fatal("expected error for non-positive SYNC_INTERVAL")
|
||||
|
|
|
|||
Loading…
Reference in New Issue