- Create modular architecture with separate packages for config, database, HTTP, logging, and state management - Implement Cobra CLI with daemon command - Set up Uber FX dependency injection - Add Chi router with health check and IP lookup endpoints - Implement GeoIP database downloader with automatic updates - Add state persistence for tracking database download times - Include comprehensive test coverage for all components - Configure structured logging with slog - Add Makefile with test, lint, and build targets - Support both IPv4 and IPv6 lookups - Return country, city, ASN, and location data in JSON format
87 lines
1.8 KiB
Go
87 lines
1.8 KiB
Go
package state
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/ipapi/internal/config"
|
|
)
|
|
|
|
func TestManager(t *testing.T) {
|
|
// Create temp directory for testing
|
|
tmpDir, err := os.MkdirTemp("", "ipapi-state-test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
cfg := &config.Config{
|
|
StateDir: tmpDir,
|
|
}
|
|
logger := slog.Default()
|
|
|
|
m, err := New(cfg, logger)
|
|
if err != nil {
|
|
t.Fatalf("failed to create manager: %v", err)
|
|
}
|
|
|
|
// Test Initialize
|
|
ctx := context.Background()
|
|
if err := m.Initialize(ctx); err != nil {
|
|
t.Fatalf("failed to initialize: %v", err)
|
|
}
|
|
|
|
// Test Load with non-existent file
|
|
state, err := m.Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load empty state: %v", err)
|
|
}
|
|
if !state.LastASNDownload.IsZero() {
|
|
t.Error("expected zero time for new state")
|
|
}
|
|
|
|
// Test Save and Load
|
|
now := time.Now().UTC()
|
|
state.LastASNDownload = now
|
|
state.LastCityDownload = now
|
|
state.LastCountryDownload = now
|
|
|
|
if err := m.Save(state); err != nil {
|
|
t.Fatalf("failed to save state: %v", err)
|
|
}
|
|
|
|
// Verify file exists
|
|
statePath := filepath.Join(tmpDir, stateFileName)
|
|
if _, err := os.Stat(statePath); os.IsNotExist(err) {
|
|
t.Error("state file was not created")
|
|
}
|
|
|
|
// Load and verify
|
|
loaded, err := m.Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load saved state: %v", err)
|
|
}
|
|
|
|
if !loaded.LastASNDownload.Equal(now) {
|
|
t.Errorf("ASN download time mismatch: got %v, want %v", loaded.LastASNDownload, now)
|
|
}
|
|
|
|
// Test update methods
|
|
if err := m.UpdateASNDownloadTime(); err != nil {
|
|
t.Fatalf("failed to update ASN download time: %v", err)
|
|
}
|
|
|
|
loaded, err = m.Load()
|
|
if err != nil {
|
|
t.Fatalf("failed to load after update: %v", err)
|
|
}
|
|
|
|
if loaded.LastASNDownload.Before(now) || loaded.LastASNDownload.Equal(now) {
|
|
t.Error("ASN download time was not updated")
|
|
}
|
|
}
|