diff --git a/supabase/functions/_backend/plugin_runtime/utils/cloudflare.ts b/supabase/functions/_backend/plugin_runtime/utils/cloudflare.ts index 41a75b8dd4..b9beeef23f 100644 --- a/supabase/functions/_backend/plugin_runtime/utils/cloudflare.ts +++ b/supabase/functions/_backend/plugin_runtime/utils/cloudflare.ts @@ -1875,8 +1875,8 @@ export async function getUpdateStatsCF(c: Context): Promise { const apps = result .filter(app => app.get > 0) .map((app) => { - const totalEvents = app.set + app.get - const successRate = Number(Number(totalEvents > 0 ? (app.get / totalEvents) * 100 : 100).toFixed(2)) + const totalOutcomes = app.set + app.failed + const successRate = Number(Number(totalOutcomes > 0 ? (app.set / totalOutcomes) * 100 : 100).toFixed(2)) return { ...app, success_rate: successRate, @@ -1891,8 +1891,8 @@ export async function getUpdateStatsCF(c: Context): Promise { return acc }, { failed: 0, set: 0, get: 0 }) - const totalEvents = total.set + total.get - const totalSuccessRate = totalEvents > 0 ? (total.get / totalEvents) * 100 : 100 + const totalOutcomes = total.set + total.failed + const totalSuccessRate = totalOutcomes > 0 ? (total.set / totalOutcomes) * 100 : 100 return { apps, @@ -2837,7 +2837,7 @@ function buildBreakdownMetrics( } export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = new Date()): Promise { - if (!c.env.APP_LOG || !c.env.DEVICE_USAGE || !c.env.DEVICE_INFO || !getEnv(c, 'CF_ANALYTICS_TOKEN') || !getEnv(c, 'CF_ACCOUNT_ANALYTICS_ID')) + if (!c.env.APP_LOG || !c.env.VERSION_USAGE || !c.env.DEVICE_USAGE || !c.env.DEVICE_INFO || !getEnv(c, 'CF_ANALYTICS_TOKEN') || !getEnv(c, 'CF_ACCOUNT_ANALYTICS_ID')) throw new Error('Public live update metric bindings are unavailable') const end = new Date(Date.UTC(referenceDate.getUTCFullYear(), referenceDate.getUTCMonth(), referenceDate.getUTCDate())) @@ -2846,17 +2846,17 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n const window = `timestamp >= toDateTime('${formatDateCF(start)}') AND timestamp < toDateTime('${formatDateCF(end)}')` const failureActions = PUBLIC_FAILURE_ACTIONS.map(action => `'${action}'`).join(', ') const day = `formatDateTime(toStartOfInterval(timestamp, INTERVAL '1' DAY), '%Y-%m-%d')` - const outcomeBase = `SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, max(if(blob2 = 'set', 1, 0)) AS succeeded, max(if(blob2 IN (${failureActions}), 1, 0)) AS failed, argMax(blob5, timestamp) AS platform, argMax(blob6, timestamp) AS country, argMax(blob7, timestamp) AS plugin_version FROM app_log WHERE ${window} AND (blob2 = 'set' OR blob2 IN (${failureActions})) GROUP BY date, app_id, device_id` - const outcomesQuery = `SELECT date, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) GROUP BY date` + const appLogOutcomeFilter = `(blob2 = 'set' OR blob2 IN (${failureActions}))` + const dailySuccessQuery = `SELECT ${day} AS date, sum(if(blob3 = 'install', 1, 0)) AS installs, sum(if(blob3 = 'fail', 1, 0)) AS fails FROM version_usage WHERE ${window} GROUP BY date ORDER BY date ASC` const failuresQuery = `SELECT action, count() AS devices FROM (SELECT ${day} AS date, blob2 AS action, index1 AS app_id, blob1 AS device_id FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, action, app_id, device_id) GROUP BY action` const platformsShareQuery = `SELECT platform, count() AS devices FROM (SELECT double1 AS platform, index1 AS app_id, blob1 AS device_id FROM device_usage WHERE ${window} AND double1 IN (0.0, 1.0, 2.0) GROUP BY platform, app_id, device_id) GROUP BY platform` - const platformsOutcomeQuery = `SELECT platform AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE platform IN ('ios', 'android', 'electron') GROUP BY platform` + const platformsOutcomeQuery = `SELECT blob5 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob5 IN ('ios', 'android', 'electron') GROUP BY blob5` const platformsFailureQuery = `SELECT platform AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob5, timestamp) AS platform FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE platform IN ('ios', 'android', 'electron') GROUP BY platform, action` const countriesShareQuery = `SELECT country AS key, count() AS devices FROM (SELECT index1 AS app_id, blob1 AS device_id, argMax(blob10, timestamp) AS country FROM device_info WHERE ${window} AND blob10 != '' GROUP BY app_id, device_id) WHERE country != '' GROUP BY country` - const countriesOutcomeQuery = `SELECT country AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE country != '' GROUP BY country` + const countriesOutcomeQuery = `SELECT blob6 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob6 != '' GROUP BY blob6` const countriesFailureQuery = `SELECT country AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob6, timestamp) AS country FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE country != '' GROUP BY country, action` const versionsShareQuery = `SELECT version AS key, count() AS devices FROM (SELECT index1 AS app_id, blob1 AS device_id, argMax(blob3, timestamp) AS version FROM device_info WHERE ${window} AND blob3 != '' GROUP BY app_id, device_id) WHERE version != '' GROUP BY version` - const versionsOutcomeQuery = `SELECT plugin_version AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE plugin_version != '' GROUP BY plugin_version` + const versionsOutcomeQuery = `SELECT blob7 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob7 != '' GROUP BY blob7` const versionsFailureQuery = `SELECT plugin_version AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob7, timestamp) AS plugin_version FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE plugin_version != '' GROUP BY plugin_version, action` try { @@ -2873,7 +2873,7 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n versionOutcomeRows, versionFailureRows, ] = await Promise.all([ - runQueryToCFA<{ date: string, successes: number, failures: number }>(c, outcomesQuery), + runQueryToCFA<{ date: string, installs: number, fails: number }>(c, dailySuccessQuery), runQueryToCFA<{ action: string, devices: number }>(c, failuresQuery), runQueryToCFA<{ platform: number, devices: number }>(c, platformsShareQuery), runQueryToCFA<{ key: string, successes: number, failures: number }>(c, platformsOutcomeQuery), @@ -2886,15 +2886,15 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n runQueryToCFA<{ key: string, action: string, devices: number }>(c, versionsFailureQuery), ]) const daily = outcomeRows.map((row) => { - const successes = Number(row.successes) || 0 - const failures = Number(row.failures) || 0 - const outcomes = successes + failures - return { date: row.date, success_rate: outcomes ? roundPublicPercent((successes / outcomes) * 100) : 0 } + const installs = Number(row.installs) || 0 + const fails = Number(row.fails) || 0 + const outcomes = installs + fails + return { date: row.date, success_rate: outcomes ? roundPublicPercent((installs / outcomes) * 100) : 0 } }).sort((a, b) => a.date.localeCompare(b.date)) - const totalSuccesses = outcomeRows.reduce((sum, row) => sum + (Number(row.successes) || 0), 0) - const totalFailures = outcomeRows.reduce((sum, row) => sum + (Number(row.failures) || 0), 0) - const totalOutcomes = totalSuccesses + totalFailures - const success_rate = totalOutcomes ? roundPublicPercent((totalSuccesses / totalOutcomes) * 100) : 0 + const totalInstalls = outcomeRows.reduce((sum, row) => sum + (Number(row.installs) || 0), 0) + const totalFails = outcomeRows.reduce((sum, row) => sum + (Number(row.fails) || 0), 0) + const totalOutcomes = totalInstalls + totalFails + const success_rate = totalOutcomes ? roundPublicPercent((totalInstalls / totalOutcomes) * 100) : 0 const failureTotal = failureRows.reduce((sum, row) => sum + (Number(row.devices) || 0), 0) const failures = [...failureRows] .map(row => ({ reason: row.action, devices: Number(row.devices) || 0 })) diff --git a/supabase/functions/_backend/utils/cloudflare.ts b/supabase/functions/_backend/utils/cloudflare.ts index 19027e1b0c..7bb6706441 100644 --- a/supabase/functions/_backend/utils/cloudflare.ts +++ b/supabase/functions/_backend/utils/cloudflare.ts @@ -2124,8 +2124,8 @@ export async function getUpdateStatsCF(c: Context): Promise { const apps = result .filter(app => app.get > 0) .map((app) => { - const totalEvents = app.set + app.get - const successRate = Number(Number(totalEvents > 0 ? (app.get / totalEvents) * 100 : 100).toFixed(2)) + const totalOutcomes = app.set + app.failed + const successRate = Number(Number(totalOutcomes > 0 ? (app.set / totalOutcomes) * 100 : 100).toFixed(2)) return { ...app, success_rate: successRate, @@ -2140,8 +2140,8 @@ export async function getUpdateStatsCF(c: Context): Promise { return acc }, { failed: 0, set: 0, get: 0 }) - const totalEvents = total.set + total.get - const totalSuccessRate = totalEvents > 0 ? (total.get / totalEvents) * 100 : 100 + const totalOutcomes = total.set + total.failed + const totalSuccessRate = totalOutcomes > 0 ? (total.set / totalOutcomes) * 100 : 100 return { apps, @@ -3086,7 +3086,7 @@ function buildBreakdownMetrics( } export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = new Date()): Promise { - if (!c.env.APP_LOG || !c.env.DEVICE_USAGE || !c.env.DEVICE_INFO || !getEnv(c, 'CF_ANALYTICS_TOKEN') || !getEnv(c, 'CF_ACCOUNT_ANALYTICS_ID')) + if (!c.env.APP_LOG || !c.env.VERSION_USAGE || !c.env.DEVICE_USAGE || !c.env.DEVICE_INFO || !getEnv(c, 'CF_ANALYTICS_TOKEN') || !getEnv(c, 'CF_ACCOUNT_ANALYTICS_ID')) throw new Error('Public live update metric bindings are unavailable') const end = new Date(Date.UTC(referenceDate.getUTCFullYear(), referenceDate.getUTCMonth(), referenceDate.getUTCDate())) @@ -3095,17 +3095,17 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n const window = `timestamp >= toDateTime('${formatDateCF(start)}') AND timestamp < toDateTime('${formatDateCF(end)}')` const failureActions = PUBLIC_FAILURE_ACTIONS.map(action => `'${action}'`).join(', ') const day = `formatDateTime(toStartOfInterval(timestamp, INTERVAL '1' DAY), '%Y-%m-%d')` - const outcomeBase = `SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, max(if(blob2 = 'set', 1, 0)) AS succeeded, max(if(blob2 IN (${failureActions}), 1, 0)) AS failed, argMax(blob5, timestamp) AS platform, argMax(blob6, timestamp) AS country, argMax(blob7, timestamp) AS plugin_version FROM app_log WHERE ${window} AND (blob2 = 'set' OR blob2 IN (${failureActions})) GROUP BY date, app_id, device_id` - const outcomesQuery = `SELECT date, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) GROUP BY date` + const appLogOutcomeFilter = `(blob2 = 'set' OR blob2 IN (${failureActions}))` + const dailySuccessQuery = `SELECT ${day} AS date, sum(if(blob3 = 'install', 1, 0)) AS installs, sum(if(blob3 = 'fail', 1, 0)) AS fails FROM version_usage WHERE ${window} GROUP BY date ORDER BY date ASC` const failuresQuery = `SELECT action, count() AS devices FROM (SELECT ${day} AS date, blob2 AS action, index1 AS app_id, blob1 AS device_id FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, action, app_id, device_id) GROUP BY action` const platformsShareQuery = `SELECT platform, count() AS devices FROM (SELECT double1 AS platform, index1 AS app_id, blob1 AS device_id FROM device_usage WHERE ${window} AND double1 IN (0.0, 1.0, 2.0) GROUP BY platform, app_id, device_id) GROUP BY platform` - const platformsOutcomeQuery = `SELECT platform AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE platform IN ('ios', 'android', 'electron') GROUP BY platform` + const platformsOutcomeQuery = `SELECT blob5 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob5 IN ('ios', 'android', 'electron') GROUP BY blob5` const platformsFailureQuery = `SELECT platform AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob5, timestamp) AS platform FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE platform IN ('ios', 'android', 'electron') GROUP BY platform, action` const countriesShareQuery = `SELECT country AS key, count() AS devices FROM (SELECT index1 AS app_id, blob1 AS device_id, argMax(blob10, timestamp) AS country FROM device_info WHERE ${window} AND blob10 != '' GROUP BY app_id, device_id) WHERE country != '' GROUP BY country` - const countriesOutcomeQuery = `SELECT country AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE country != '' GROUP BY country` + const countriesOutcomeQuery = `SELECT blob6 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob6 != '' GROUP BY blob6` const countriesFailureQuery = `SELECT country AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob6, timestamp) AS country FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE country != '' GROUP BY country, action` const versionsShareQuery = `SELECT version AS key, count() AS devices FROM (SELECT index1 AS app_id, blob1 AS device_id, argMax(blob3, timestamp) AS version FROM device_info WHERE ${window} AND blob3 != '' GROUP BY app_id, device_id) WHERE version != '' GROUP BY version` - const versionsOutcomeQuery = `SELECT plugin_version AS key, sum(succeeded) AS successes, sum(if(succeeded = 0, failed, 0)) AS failures FROM (${outcomeBase}) WHERE plugin_version != '' GROUP BY plugin_version` + const versionsOutcomeQuery = `SELECT blob7 AS key, sum(if(blob2 = 'set', 1, 0)) AS successes, sum(if(blob2 IN (${failureActions}), 1, 0)) AS failures FROM app_log WHERE ${window} AND ${appLogOutcomeFilter} AND blob7 != '' GROUP BY blob7` const versionsFailureQuery = `SELECT plugin_version AS key, action, count() AS devices FROM (SELECT ${day} AS date, index1 AS app_id, blob1 AS device_id, blob2 AS action, argMax(blob7, timestamp) AS plugin_version FROM app_log WHERE ${window} AND blob2 IN (${failureActions}) GROUP BY date, app_id, device_id, action) WHERE plugin_version != '' GROUP BY plugin_version, action` try { @@ -3122,7 +3122,7 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n versionOutcomeRows, versionFailureRows, ] = await Promise.all([ - runQueryToCFA<{ date: string, successes: number, failures: number }>(c, outcomesQuery), + runQueryToCFA<{ date: string, installs: number, fails: number }>(c, dailySuccessQuery), runQueryToCFA<{ action: string, devices: number }>(c, failuresQuery), runQueryToCFA<{ platform: number, devices: number }>(c, platformsShareQuery), runQueryToCFA<{ key: string, successes: number, failures: number }>(c, platformsOutcomeQuery), @@ -3135,15 +3135,15 @@ export async function getPublicLiveUpdateMetricsCF(c: Context, referenceDate = n runQueryToCFA<{ key: string, action: string, devices: number }>(c, versionsFailureQuery), ]) const daily = outcomeRows.map((row) => { - const successes = Number(row.successes) || 0 - const failures = Number(row.failures) || 0 - const outcomes = successes + failures - return { date: row.date, success_rate: outcomes ? roundPublicPercent((successes / outcomes) * 100) : 0 } + const installs = Number(row.installs) || 0 + const fails = Number(row.fails) || 0 + const outcomes = installs + fails + return { date: row.date, success_rate: outcomes ? roundPublicPercent((installs / outcomes) * 100) : 0 } }).sort((a, b) => a.date.localeCompare(b.date)) - const totalSuccesses = outcomeRows.reduce((sum, row) => sum + (Number(row.successes) || 0), 0) - const totalFailures = outcomeRows.reduce((sum, row) => sum + (Number(row.failures) || 0), 0) - const totalOutcomes = totalSuccesses + totalFailures - const success_rate = totalOutcomes ? roundPublicPercent((totalSuccesses / totalOutcomes) * 100) : 0 + const totalInstalls = outcomeRows.reduce((sum, row) => sum + (Number(row.installs) || 0), 0) + const totalFails = outcomeRows.reduce((sum, row) => sum + (Number(row.fails) || 0), 0) + const totalOutcomes = totalInstalls + totalFails + const success_rate = totalOutcomes ? roundPublicPercent((totalInstalls / totalOutcomes) * 100) : 0 const failureTotal = failureRows.reduce((sum, row) => sum + (Number(row.devices) || 0), 0) const failures = [...failureRows] .map(row => ({ reason: row.action, devices: Number(row.devices) || 0 })) diff --git a/supabase/functions/_backend/utils/supabase.ts b/supabase/functions/_backend/utils/supabase.ts index b5ccea5e92..4214ec84ab 100644 --- a/supabase/functions/_backend/utils/supabase.ts +++ b/supabase/functions/_backend/utils/supabase.ts @@ -1757,8 +1757,8 @@ export async function getUpdateStatsSB(c: Context): Promise { } const apps = data.map((app: any) => { - const totalEvents = app.failed + app.install + app.get - const successRate = Number((totalEvents > 0 ? ((app.install + app.get) / totalEvents) * 100 : 100).toFixed(2)) + const totalOutcomes = app.failed + app.install + const successRate = Number((totalOutcomes > 0 ? (app.install / totalOutcomes) * 100 : 100).toFixed(2)) return { app_id: app.app_id, failed: Number(app.failed), @@ -1776,8 +1776,8 @@ export async function getUpdateStatsSB(c: Context): Promise { return acc }, { failed: 0, set: 0, get: 0 }) - const totalEvents = total.failed + total.set + total.get - const totalSuccessRate = totalEvents > 0 ? ((total.set + total.get) / totalEvents) * 100 : 100 + const totalOutcomes = total.failed + total.set + const totalSuccessRate = totalOutcomes > 0 ? (total.set / totalOutcomes) * 100 : 100 return { apps, diff --git a/supabase/migrations/20260811083707_fix_update_stats_success_rate_formula.sql b/supabase/migrations/20260811083707_fix_update_stats_success_rate_formula.sql new file mode 100644 index 0000000000..4dc75b5e66 --- /dev/null +++ b/supabase/migrations/20260811083707_fix_update_stats_success_rate_formula.sql @@ -0,0 +1,43 @@ +CREATE OR REPLACE FUNCTION "public"."get_update_stats"() RETURNS TABLE("app_id" character varying, "failed" bigint, "install" bigint, "get" bigint, "success_rate" numeric, "healthy" boolean) + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +BEGIN + RETURN QUERY + WITH stats AS ( + SELECT + version_usage.app_id, + COALESCE(SUM(CASE WHEN action = 'fail' THEN 1 ELSE 0 END), 0) AS failed, + COALESCE(SUM(CASE WHEN action = 'install' THEN 1 ELSE 0 END), 0) AS install, + COALESCE(SUM(CASE WHEN action = 'get' THEN 1 ELSE 0 END), 0) AS get + FROM + public.version_usage + WHERE + timestamp >= (date_trunc('minute', now()) - INTERVAL '10 minutes') + AND timestamp < (date_trunc('minute', now()) - INTERVAL '9 minutes') + GROUP BY + version_usage.app_id + ) + SELECT + stats.app_id, + stats.failed, + stats.install, + stats.get, + CASE + WHEN (stats.install + stats.failed) > 0 THEN + ROUND((stats.install::numeric / (stats.install + stats.failed)) * 100, 2) + ELSE 100 + END AS success_rate, + CASE + WHEN (stats.install + stats.failed) > 0 THEN + ((stats.install::numeric / (stats.install + stats.failed)) * 100 >= 70) + ELSE true + END AS healthy + FROM + stats + WHERE + stats.get > 0; +END; +$$; + +ALTER FUNCTION "public"."get_update_stats"() OWNER TO "postgres"; diff --git a/tests/public-live-update-metrics.unit.test.ts b/tests/public-live-update-metrics.unit.test.ts index 5c06f5ac4a..2ac089f9ff 100644 --- a/tests/public-live-update-metrics.unit.test.ts +++ b/tests/public-live-update-metrics.unit.test.ts @@ -17,6 +17,7 @@ function createContext() { return { env: { APP_LOG: {}, + VERSION_USAGE: {}, DEVICE_USAGE: {}, DEVICE_INFO: {}, CF_ANALYTICS_TOKEN: 'analytics-token', @@ -44,14 +45,14 @@ describe('public live update metrics', () => { const query = String(init?.body ?? '') queries.push(query) - if (query.includes('SELECT date, sum(succeeded) AS successes') && query.includes('GROUP BY date')) { + if (query.includes('FROM version_usage') && query.includes('AS installs')) { return analyticsResponse( [ { name: 'date', type: 'String' }, - { name: 'successes', type: 'UInt64' }, - { name: 'failures', type: 'UInt64' }, + { name: 'installs', type: 'UInt64' }, + { name: 'fails', type: 'UInt64' }, ], - [{ date: '2026-06-30', successes: '90', failures: '10' }], + [{ date: '2026-06-30', installs: '90', fails: '10' }], ) } @@ -79,7 +80,7 @@ describe('public live update metrics', () => { ) } - if (query.includes('platform AS key') && query.includes('sum(succeeded)')) { + if (query.includes('blob5 AS key') && query.includes('sum(if(blob2 = \'set\', 1, 0))')) { return analyticsResponse( [ { name: 'key', type: 'String' }, @@ -121,7 +122,7 @@ describe('public live update metrics', () => { ) } - if (query.includes('country AS key') && query.includes('sum(succeeded)')) { + if (query.includes('blob6 AS key') && query.includes('sum(if(blob2 = \'set\', 1, 0))')) { return analyticsResponse( [ { name: 'key', type: 'String' }, @@ -162,7 +163,7 @@ describe('public live update metrics', () => { ) } - if (query.includes('plugin_version AS key') && query.includes('sum(succeeded)')) { + if (query.includes('blob7 AS key') && query.includes('sum(if(blob2 = \'set\', 1, 0))')) { return analyticsResponse( [ { name: 'key', type: 'String' }, @@ -209,6 +210,7 @@ describe('public live update metrics', () => { expect(metrics.updater_versions[0]).toMatchObject({ key: '8.1.0', share: 60 }) expect(metrics.updater_versions.find(row => row.key === '8.1.0')?.success_rate).toBe(93.3) expect(queries.length).toBe(11) + expect(queries.join('\n')).toContain('FROM version_usage') expect(queries.join('\n')).toContain('blob6') expect(queries.join('\n')).toContain('blob7') expect(queries.join('\n')).toContain('blob10')