package geo import ( "context" "database/sql" "errors" "testing" "github.com/google/uuid" "go.uber.org/zap" ) // NewServiceForTest builds a Service with no GeoLite2 resolver. It is // the entry point external tests use when they want to exercise the // counter / admin paths without spinning up a real mmdb file. The // returned Service still owns its background context and logger so // IncrementCounterAsync and ListUserCounters behave exactly as they do // in production. func NewServiceForTest(db *sql.DB) (*Service, error) { if db == nil { return nil, errors.New("geo: db must not be nil") } bgCtx, bgCancel := context.WithCancel(context.Background()) return &Service{ db: db, logger: zap.NewNop(), bgCtx: bgCtx, bgCancel: bgCancel, }, nil } // IncrementCounterTestSync runs the package-private upsert path // synchronously so external tests can assert on counter rows without // having to deal with goroutine scheduling. Failure to upsert fails the // test rather than being silently logged. func (s *Service) IncrementCounterTestSync(t *testing.T, userID uuid.UUID, country string) { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), counterUpsertTimeout) defer cancel() if err := s.upsertCounter(ctx, userID, country); err != nil { t.Fatalf("upsert counter (%s/%s): %v", userID, country, err) } }