Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion apps/sim/triggers/constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import { POLLING_PROVIDERS } from '@/triggers/constants'
import { getProviderHandler } from '@/lib/webhooks/providers'
import { isInternalTriggerProvider, POLLING_PROVIDERS } from '@/triggers/constants'
import { TRIGGER_REGISTRY } from '@/triggers/registry'

describe('POLLING_PROVIDERS sync with TriggerConfig.polling', () => {
Expand All @@ -26,6 +27,40 @@ describe('POLLING_PROVIDERS sync with TriggerConfig.polling', () => {
expect(missing, `Triggers with polling: true missing from POLLING_PROVIDERS`).toEqual([])
})

/**
* `acceptsPathWebhookDelivery` gates the whole PROVIDER, not the trigger id, so a provider that
* serves the public path route must not also own a polling trigger - membership in
* `POLLING_PROVIDERS` would 404 its real deliveries. Providers gated wholesale for a
* provider-level reason (internal, or an app-level ingress route) never serve that route, so
* mixing is harmless there and they are exempt. Split dual-delivery services into two providers
* instead, as Slack does with `slack` and `slack_app`.
*/
it('no path-delivered provider also owns a polling trigger', () => {
const byProvider = new Map<string, { polling: string[]; path: string[] }>()
for (const trigger of Object.values(TRIGGER_REGISTRY)) {
const gatedByProvider =
isInternalTriggerProvider(trigger.provider) ||
getProviderHandler(trigger.provider).ingressMode === 'provider'
if (gatedByProvider) continue

const entry = byProvider.get(trigger.provider) ?? { polling: [], path: [] }
entry[trigger.polling === true ? 'polling' : 'path'].push(trigger.id)
byProvider.set(trigger.provider, entry)
}

const mixed = [...byProvider]
.filter(([, entry]) => entry.polling.length > 0 && entry.path.length > 0)
.map(
([provider, entry]) =>
`${provider}: polling=[${entry.polling.join(', ')}] path=[${entry.path.join(', ')}]`
)

expect(
mixed,
'Split the path-delivered triggers onto their own provider - the public trigger route rejects the whole provider'
).toEqual([])
})

it('no POLLING_PROVIDERS entry lacks a polling: true trigger in the registry', () => {
const extra: string[] = []
for (const provider of POLLING_PROVIDERS) {
Expand Down
Loading