diff --git a/apps/backend/internal/integrations/healthpoll/healthpoll.go b/apps/backend/internal/integrations/healthpoll/healthpoll.go index 3fdcf88084..c5a853672e 100644 --- a/apps/backend/internal/integrations/healthpoll/healthpoll.go +++ b/apps/backend/internal/integrations/healthpoll/healthpoll.go @@ -103,7 +103,7 @@ func (p *Poller) loop(ctx context.Context) { defer p.wg.Done() // Run an initial probe immediately so the UI gets a status without // waiting the full interval after backend startup. - p.ProbeAll(ctx) + p.probeAll(ctx) ticker := time.NewTicker(p.interval) defer ticker.Stop() for { @@ -111,14 +111,14 @@ func (p *Poller) loop(ctx context.Context) { case <-ctx.Done(): return case <-ticker.C: - p.ProbeAll(ctx) + p.probeAll(ctx) } } } -// ProbeAll runs one probe pass across every configured workspace. Exposed so -// tests can drive it deterministically without having to wait for a tick. -func (p *Poller) ProbeAll(ctx context.Context) { +// probeAll runs one probe pass across every configured workspace. Used by the +// loop and by in-package tests that want to drive a probe deterministically. +func (p *Poller) probeAll(ctx context.Context) { ids, err := p.prober.ListConfiguredWorkspaces(ctx) if err != nil { p.logger.Warn(p.name+" poller: list workspaces failed", zap.Error(err)) diff --git a/apps/backend/internal/integrations/healthpoll/healthpoll_test.go b/apps/backend/internal/integrations/healthpoll/healthpoll_test.go index 95f60dd2d8..3949d5fae1 100644 --- a/apps/backend/internal/integrations/healthpoll/healthpoll_test.go +++ b/apps/backend/internal/integrations/healthpoll/healthpoll_test.go @@ -46,7 +46,7 @@ func TestProbeAll_RecordsEachWorkspace(t *testing.T) { } poller := New("test", p, logger.Default()) - poller.ProbeAll(context.Background()) + poller.probeAll(context.Background()) p.mu.Lock() defer p.mu.Unlock() @@ -63,7 +63,7 @@ func TestProbeAll_ListError_DoesNotPanic(t *testing.T) { } poller := New("test", p, logger.Default()) - poller.ProbeAll(context.Background()) // expect: silent recovery + warning log + poller.probeAll(context.Background()) // expect: silent recovery + warning log p.mu.Lock() defer p.mu.Unlock() @@ -82,7 +82,7 @@ func TestProbeAll_StopsWhenContextCancelled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() // cancel before the loop runs - poller.ProbeAll(ctx) + poller.probeAll(ctx) p.mu.Lock() defer p.mu.Unlock() diff --git a/apps/web/components/jira/jira-settings.tsx b/apps/web/components/jira/jira-settings.tsx index a0f973e56b..1be7b47821 100644 --- a/apps/web/components/jira/jira-settings.tsx +++ b/apps/web/components/jira/jira-settings.tsx @@ -19,6 +19,7 @@ import { IntegrationAuthStatusBanner, type IntegrationAuthHealth, } from "@/components/integrations/auth-status-banner"; +import { INTEGRATION_STATUS_REFRESH_MS } from "@/hooks/domains/integrations/use-integration-availability"; import { getJiraConfig, setJiraConfig, @@ -27,10 +28,6 @@ import { } from "@/lib/api/domains/jira-api"; import type { JiraAuthMethod, JiraConfig, TestJiraConnectionResult } from "@/lib/types/jira"; -// How often to re-fetch the config so the auth-health indicator picks up new -// probe results from the backend poller (which runs every 90s). -const STATUS_REFRESH_MS = 90_000; - // Session cookies are HttpOnly so document.cookie can't read them, but // DevTools → Application → Cookies surfaces them in plain text. Users copy // the Value cell of a single row; the backend wraps it under both @@ -368,7 +365,7 @@ function useJiraSettings(workspaceId: string) { .catch(() => { /* transient failures are fine — next tick retries */ }); - }, STATUS_REFRESH_MS); + }, INTEGRATION_STATUS_REFRESH_MS); return () => clearInterval(id); }, [workspaceId]); diff --git a/apps/web/components/linear/linear-settings.tsx b/apps/web/components/linear/linear-settings.tsx index 3cb8241593..f163f62489 100644 --- a/apps/web/components/linear/linear-settings.tsx +++ b/apps/web/components/linear/linear-settings.tsx @@ -17,6 +17,7 @@ import { IntegrationAuthStatusBanner, type IntegrationAuthHealth, } from "@/components/integrations/auth-status-banner"; +import { INTEGRATION_STATUS_REFRESH_MS } from "@/hooks/domains/integrations/use-integration-availability"; import { getLinearConfig, setLinearConfig, @@ -26,10 +27,6 @@ import { } from "@/lib/api/domains/linear-api"; import type { LinearConfig, LinearTeam, TestLinearConnectionResult } from "@/lib/types/linear"; -// How often to re-fetch the config so the auth-health indicator picks up new -// probe results from the backend poller (which runs every 90s). -const STATUS_REFRESH_MS = 90_000; - type FormState = { defaultTeamKey: string; secret: string; @@ -336,7 +333,7 @@ function useLinearSettings(workspaceId: string) { .catch(() => { /* transient failures are fine — next tick retries */ }); - }, STATUS_REFRESH_MS); + }, INTEGRATION_STATUS_REFRESH_MS); return () => clearInterval(id); }, [workspaceId]);