tests/channel-scoped-stats.unit.test.ts
Rama main · réplica de lectura del repositorio original.
import type { Context } from 'hono'
import { describe, expect, it, vi } from 'vitest'
import * as pluginRuntimeCloudflare from '../supabase/functions/_backend/plugin_runtime/utils/cloudflare.ts'
import { buildDeviceChannelScopeCF, buildDeviceVersionCountsCFQuery, buildVersionUsageChannelFilterCF } from '../supabase/functions/_backend/utils/cloudflare.ts'
import { countChannelScopedDeviceVersions, MAX_CHANNEL_DEVICE_OVERRIDES_FOR_COUNTS, partitionChannelDeviceOverrides, pickChannelDefaultPlatforms, readDeviceVersionCountsSB } from '../supabase/functions/_backend/utils/supabase.ts'
const pgMocks = vi.hoisted(() => ({
getPgClient: vi.fn(),
}))
vi.mock('../supabase/functions/_backend/utils/pg.ts', async importOriginal => ({
...await importOriginal<typeof import('../supabase/functions/_backend/utils/pg.ts')>(),
getPgClient: pgMocks.getPgClient,
closeClient: vi.fn(),
}))
describe('version_usage channel filter', () => {
it.concurrent('matches channel id with a legacy name fallback', () => {
expect(buildVersionUsageChannelFilterCF({ id: 42, name: 'production' }))
.toBe('AND (blob5 = \'42\' OR (blob5 = \'\' AND blob4 = \'production\'))')
})
it.concurrent('supports id-only, name-only and no channel', () => {
expect(buildVersionUsageChannelFilterCF({ id: 42 })).toBe('AND blob5 = \'42\'')
expect(buildVersionUsageChannelFilterCF({ name: 'beta' })).toBe('AND blob4 = \'beta\'')
expect(buildVersionUsageChannelFilterCF('beta')).toBe('AND blob4 = \'beta\'')
expect(buildVersionUsageChannelFilterCF(undefined)).toBe('')
expect(buildVersionUsageChannelFilterCF({ id: null, name: null })).toBe('')
})
it.concurrent('optionally keeps legacy get rows that carry no channel', () => {
expect(buildVersionUsageChannelFilterCF({ id: 42, name: 'production' }, { includeUnattributedGets: true }))
.toBe('AND ((blob5 = \'42\' OR (blob5 = \'\' AND blob4 = \'production\')) OR (blob3 = \'get\' AND blob4 = \'\' AND blob5 = \'\'))')
expect(buildVersionUsageChannelFilterCF(undefined, { includeUnattributedGets: true })).toBe('')
})
it.concurrent('escapes channel names', () => {
expect(buildVersionUsageChannelFilterCF({ id: 1, name: 'x\' OR 1=1 --' })).toContain('blob4 = \'x\'\' OR 1=1 --\'')
})
it.concurrent('keeps the plugin_runtime copy in sync', () => {
expect(pluginRuntimeCloudflare.buildVersionUsageChannelFilterCF({ id: 42, name: 'production' }))
.toBe(buildVersionUsageChannelFilterCF({ id: 42, name: 'production' }))
const overrides = { into: ['a'], elsewhere: ['b'], defaultForPlatforms: ['ios' as const] }
expect(pluginRuntimeCloudflare.buildDeviceVersionCountsCFQuery('com.app', 'production', overrides))
.toBe(buildDeviceVersionCountsCFQuery('com.app', 'production', overrides))
})
})
describe('device version counts channel scope', () => {
it.concurrent('keeps the default_channel filter when there are no overrides', () => {
expect(buildDeviceChannelScopeCF('production')).toBe('default_channel = \'production\'')
expect(buildDeviceChannelScopeCF('production', { into: [], elsewhere: [] })).toBe('default_channel = \'production\'')
expect(buildDeviceVersionCountsCFQuery('com.app')).not.toContain('default_channel = ')
})
it.concurrent('adds forced-in devices and removes devices forced elsewhere', () => {
const scope = buildDeviceChannelScopeCF('production', { into: ['in-1', 'in-2'], elsewhere: ['out-1'] })
expect(scope).toBe('((default_channel = \'production\' AND lower(device_id) NOT IN (\'out-1\')) OR lower(device_id) IN (\'in-1\', \'in-2\'))')
const query = buildDeviceVersionCountsCFQuery('com.app', 'production', { into: ['in-1'], elsewhere: [] })
expect(query).toContain('WHERE version_name != \'\' AND (default_channel = \'production\' OR lower(device_id) IN (\'in-1\'))')
})
it.concurrent('counts devices without a reported channel on platforms where the channel is the public default', () => {
expect(buildDeviceChannelScopeCF('production', { into: [], elsewhere: [], defaultForPlatforms: ['ios', 'android'] }))
.toBe('(default_channel = \'production\' OR (default_channel = \'\' AND platform IN (1, 0)))')
const scope = buildDeviceChannelScopeCF('production', { into: ['in-1'], elsewhere: ['out-1'], defaultForPlatforms: ['electron'] })
expect(scope).toBe('(((default_channel = \'production\' OR (default_channel = \'\' AND platform IN (2))) AND lower(device_id) NOT IN (\'out-1\')) OR lower(device_id) IN (\'in-1\'))')
expect(buildDeviceVersionCountsCFQuery('com.app', 'production')).toContain('argMax(double1, timestamp) AS platform')
})
it.concurrent('escapes device ids and channel names', () => {
const scope = buildDeviceChannelScopeCF('prod\'uction', { into: ['a\'b'], elsewhere: ['c\'d'] })
expect(scope).toContain('default_channel = \'prod\'\'uction\'')
expect(scope).toContain('IN (\'a\'\'b\')')
expect(scope).toContain('NOT IN (\'c\'\'d\')')
})
})
describe('public default channel platforms', () => {
// Sorted by name then id, like the /updates fallback.
const rows = [
{ id: 3, name: 'android-prod', ios: false, android: true, electron: false },
{ id: 1, name: 'production', ios: true, android: true, electron: true },
{ id: 2, name: 'zeta', ios: true, android: false, electron: false },
]
it.concurrent('returns the platforms the channel serves by default', () => {
expect(pickChannelDefaultPlatforms(rows, { id: 1, name: 'production' })).toEqual(['ios', 'electron'])
expect(pickChannelDefaultPlatforms(rows, { id: 3 })).toEqual(['android'])
expect(pickChannelDefaultPlatforms(rows, 'zeta')).toEqual([])
expect(pickChannelDefaultPlatforms([], 'production')).toEqual([])
})
})
describe('channel_devices override partitioning', () => {
const rows = [
{ device_id: 'DEV-IN', channel_id: 1, channels: { name: 'production' } },
{ device_id: 'dev-out', channel_id: 2, channels: { name: 'beta' } },
{ device_id: 'dev-out-2', channel_id: 3, channels: [{ name: 'qa' }] },
]
it.concurrent('matches by channel id when known and lowercases ids', () => {
expect(partitionChannelDeviceOverrides(rows, { id: 1, name: 'production' })).toEqual({
into: ['dev-in'],
elsewhere: ['dev-out', 'dev-out-2'],
truncated: false,
})
})
it.concurrent('matches by name when only the name is known', () => {
expect(partitionChannelDeviceOverrides(rows, 'beta')).toEqual({
into: ['dev-out'],
elsewhere: ['dev-in', 'dev-out-2'],
truncated: false,
})
})
it.concurrent('caps the combined list, keeping forced-in devices first', () => {
const many = Array.from({ length: 5 }, (_, index) => ({ device_id: `in-${index}`, channel_id: 1 }))
.concat(Array.from({ length: 5 }, (_, index) => ({ device_id: `out-${index}`, channel_id: 2 })))
const result = partitionChannelDeviceOverrides(many, { id: 1 }, 7)
expect(result.into).toHaveLength(5)
expect(result.elsewhere).toHaveLength(2)
expect(result.truncated).toBe(true)
expect(MAX_CHANNEL_DEVICE_OVERRIDES_FOR_COUNTS).toBeGreaterThan(0)
})
it.concurrent('counts devices by effective channel for the Supabase fallback', () => {
const counts = countChannelScopedDeviceVersions(
[
{ device_id: 'a', version_name: '1.0.0' },
{ device_id: 'B', version_name: '1.0.0' },
{ device_id: 'c', version_name: null },
],
[
{ device_id: 'forced', version_name: '2.0.0' },
{ device_id: 'a', version_name: '1.0.0' },
],
{ into: ['forced', 'a'], elsewhere: ['b'] },
)
expect(counts).toEqual({ '1.0.0': 1, 'unknown': 1, '2.0.0': 1 })
})
})
interface FakeDevice {
id: number
device_id: string
app_id: string
platform: string
default_channel: string | null
version_name: string
}
// Minimal node-postgres client over an in-memory devices table. Records a
// label for every query so tests can assert the queries that were sent.
function createFakeDevicesClient(devices: FakeDevice[]) {
const requests: string[] = []
const pick = (rows: FakeDevice[]) => ({ rows: rows.map(row => ({ device_id: row.device_id, version_name: row.version_name })) })
const client = {
async query(text: string, values: unknown[]) {
expect(text).toContain('FROM public.devices')
const appRows = devices.filter(row => row.app_id === values[0])
if (text.includes('default_channel IS NULL OR default_channel = \'\'')) {
const platforms = values[1] as string[]
requests.push(`unreported:${platforms.join(',')}`)
return pick(appRows.filter(row => (row.default_channel === null || row.default_channel === '') && platforms.includes(row.platform)))
}
if (text.includes('device_id = ANY')) {
const ids = values[1] as string[]
requests.push(`forced:${ids.join(',')}`)
return pick(appRows.filter(row => ids.includes(row.device_id)))
}
if (text.includes('default_channel = $2')) {
requests.push(`channel:${values[1]}`)
return pick(appRows.filter(row => row.default_channel === values[1]))
}
requests.push('all')
return pick(appRows)
},
}
return { client, requests }
}
describe('readDeviceVersionCountsSB', () => {
const c = { get: () => 'test-request' } as unknown as Context
let nextId = 1
const device = (device_id: string, platform: string, default_channel: string | null, version_name: string, app_id = 'com.app'): FakeDevice =>
({ id: nextId++, device_id, app_id, platform, default_channel, version_name })
it('adds devices without a reported channel on the default platforms', async () => {
const { client, requests } = createFakeDevicesClient([
device('pinned', 'android', 'production', '1.0.0'),
device('implicit-android', 'android', null, '1.0.0'),
device('implicit-empty', 'android', '', '1.1.0'),
device('implicit-ios', 'ios', null, '1.0.0'),
device('other-channel', 'android', 'beta', '1.0.0'),
device('other-app', 'android', null, '1.0.0', 'com.other'),
])
pgMocks.getPgClient.mockResolvedValueOnce(client)
const counts = await readDeviceVersionCountsSB(c, 'com.app', 'production', { into: [], elsewhere: [], defaultForPlatforms: ['android'] })
expect(counts).toEqual({ '1.0.0': 2, '1.1.0': 1 })
expect(requests).toEqual(['channel:production', 'unreported:android'])
})
it('skips the unreported query when the channel is no platform default', async () => {
const { client, requests } = createFakeDevicesClient([
device('pinned', 'android', 'production', '1.0.0'),
device('implicit-android', 'android', null, '1.0.0'),
])
pgMocks.getPgClient.mockResolvedValueOnce(client)
expect(await readDeviceVersionCountsSB(c, 'com.app', 'production', { into: [], elsewhere: [] })).toEqual({ '1.0.0': 1 })
expect(requests).toEqual(['channel:production'])
})
it('reads devices forced into the channel and drops devices forced elsewhere', async () => {
const { client, requests } = createFakeDevicesClient([
device('pinned', 'android', 'production', '1.0.0'),
device('moved-away', 'android', 'production', '1.0.0'),
device('forced-in', 'ios', 'beta', '2.0.0'),
])
pgMocks.getPgClient.mockResolvedValueOnce(client)
const counts = await readDeviceVersionCountsSB(c, 'com.app', 'production', { into: ['forced-in'], elsewhere: ['moved-away'] })
expect(counts).toEqual({ '1.0.0': 1, '2.0.0': 1 })
expect(requests).toEqual(['channel:production', 'forced:forced-in'])
})
it('counts every matching device without a row cap', async () => {
const devices = Array.from({ length: 2500 }, (_, index) => device(`implicit-${index}`, 'android', null, index < 1000 ? '1.0.0' : '1.1.0'))
const { client, requests } = createFakeDevicesClient(devices)
pgMocks.getPgClient.mockResolvedValueOnce(client)
const counts = await readDeviceVersionCountsSB(c, 'com.app', 'production', { into: [], elsewhere: [], defaultForPlatforms: ['android'] })
expect(counts).toEqual({ '1.0.0': 1000, '1.1.0': 1500 })
expect(requests.filter(request => request.startsWith('unreported:'))).toHaveLength(1)
})
})