diff --git a/.gitignore b/.gitignore index 4c49bd7..93adf98 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +rota.json diff --git a/live_test.go b/live_test.go new file mode 100644 index 0000000..8c2a209 --- /dev/null +++ b/live_test.go @@ -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) +} diff --git a/service.go b/service.go index e3c492d..9aec79e 100644 --- a/service.go +++ b/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") diff --git a/service_test.go b/service_test.go index 587bd7d..9711797 100644 --- a/service_test.go +++ b/service_test.go @@ -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")