fix(api): align /devices version scope, add Vary header, add validation + tests
This commit is contained in:
@@ -77,4 +77,40 @@ describe("buildPluginGameResponse — shape contract", () => {
|
|||||||
expect(r.topEntries.length).toBe(2)
|
expect(r.topEntries.length).toBe(2)
|
||||||
expect(r.topEntries[0].id).toBe("e1") // pinned first
|
expect(r.topEntries[0].id).toBe("e1") // pinned first
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("handles all-null fps values gracefully", async () => {
|
||||||
|
const entries = [
|
||||||
|
{ id: "e1", hardwareSlug: "steamdeck-oled", fpsAvg: 60, fpsLow: null, fpsOnePercentLow: null, fpsHigh: null,
|
||||||
|
upscalerType: "none", frameGenMethod: "none", protonVersion: null, osVersion: null, tdpWatts: null,
|
||||||
|
settingsJson: null, upvotes: 0, isPinned: false, createdAt: new Date("2026-01-01"),
|
||||||
|
userName: null, userImage: null },
|
||||||
|
]
|
||||||
|
const r = await buildPluginGameResponse({ game: { id: "g1", steamAppId: 123, title: "X", slug: "x" }, entries, recent: entries })
|
||||||
|
expect(r.estFps).not.toBeNull()
|
||||||
|
expect(r.estFps!.avg).toBe(60)
|
||||||
|
expect(r.estFps!.low).toBeNull()
|
||||||
|
expect(r.estFps!.onePct).toBeNull()
|
||||||
|
expect(r.estFps!.high).toBeNull()
|
||||||
|
expect(r.estFps!.count).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("trims recent entries separately from topEntries", async () => {
|
||||||
|
const top = [
|
||||||
|
{ id: "e1", hardwareSlug: "steamdeck-oled", fpsAvg: 60, fpsLow: 40, fpsOnePercentLow: 45, fpsHigh: 90,
|
||||||
|
upscalerType: "none", frameGenMethod: "none", protonVersion: "9", osVersion: "SteamOS 3", tdpWatts: 12,
|
||||||
|
settingsJson: null, upvotes: 5, isPinned: true, createdAt: new Date("2026-01-01"),
|
||||||
|
userName: "u", userImage: null },
|
||||||
|
]
|
||||||
|
const recent = [
|
||||||
|
{ id: "e2", hardwareSlug: "steamdeck-oled", fpsAvg: 80, fpsLow: 55, fpsOnePercentLow: 60, fpsHigh: 120,
|
||||||
|
upscalerType: "fsr", frameGenMethod: "none", protonVersion: "9", osVersion: "SteamOS 3", tdpWatts: 15,
|
||||||
|
settingsJson: null, upvotes: 2, isPinned: false, createdAt: new Date("2026-02-01"),
|
||||||
|
userName: "u2", userImage: null },
|
||||||
|
]
|
||||||
|
const r = await buildPluginGameResponse({ game: { id: "g1", steamAppId: 123, title: "X", slug: "x" }, entries: top, recent })
|
||||||
|
expect(r.topEntries).toHaveLength(1)
|
||||||
|
expect(r.topEntries[0].id).toBe("e1")
|
||||||
|
expect(r.recentEntries).toHaveLength(1)
|
||||||
|
expect(r.recentEntries[0].id).toBe("e2")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -207,6 +207,7 @@ export const pluginPublicRoutes = new Elysia({
|
|||||||
.limit(query.limit ?? 3)
|
.limit(query.limit ?? 3)
|
||||||
|
|
||||||
set.headers["Cache-Control"] = "public, max-age=60"
|
set.headers["Cache-Control"] = "public, max-age=60"
|
||||||
|
set.headers["Vary"] = "search-params"
|
||||||
return await buildPluginGameResponse({
|
return await buildPluginGameResponse({
|
||||||
game,
|
game,
|
||||||
entries: topRows as unknown as PluginEntryRow[],
|
entries: topRows as unknown as PluginEntryRow[],
|
||||||
@@ -230,6 +231,10 @@ export const pluginPublicRoutes = new Elysia({
|
|||||||
"/game/:steamAppId/devices",
|
"/game/:steamAppId/devices",
|
||||||
async ({ params, set }) => {
|
async ({ params, set }) => {
|
||||||
const steamAppId = Number(params.steamAppId)
|
const steamAppId = Number(params.steamAppId)
|
||||||
|
if (!Number.isInteger(steamAppId) || steamAppId <= 0) {
|
||||||
|
set.status = 400
|
||||||
|
return { error: "Invalid steamAppId", devices: [] }
|
||||||
|
}
|
||||||
const [game] = await db
|
const [game] = await db
|
||||||
.select({ id: games.id })
|
.select({ id: games.id })
|
||||||
.from(games)
|
.from(games)
|
||||||
@@ -248,10 +253,17 @@ export const pluginPublicRoutes = new Elysia({
|
|||||||
.from(performanceEntries)
|
.from(performanceEntries)
|
||||||
.innerJoin(hardware, eq(performanceEntries.hardwareSlug, hardware.slug))
|
.innerJoin(hardware, eq(performanceEntries.hardwareSlug, hardware.slug))
|
||||||
.innerJoin(gameVersions, eq(performanceEntries.versionId, gameVersions.id))
|
.innerJoin(gameVersions, eq(performanceEntries.versionId, gameVersions.id))
|
||||||
.where(and(eq(gameVersions.gameId, game.id), eq(performanceEntries.isRemoved, false)))
|
.where(
|
||||||
|
and(
|
||||||
|
eq(gameVersions.gameId, game.id),
|
||||||
|
eq(gameVersions.isLatest, true),
|
||||||
|
eq(performanceEntries.isRemoved, false),
|
||||||
|
),
|
||||||
|
)
|
||||||
.groupBy(performanceEntries.hardwareSlug, hardware.name)
|
.groupBy(performanceEntries.hardwareSlug, hardware.name)
|
||||||
.orderBy(desc(sql`count(*)`))
|
.orderBy(desc(sql`count(*)`))
|
||||||
set.headers["Cache-Control"] = "public, max-age=60"
|
set.headers["Cache-Control"] = "public, max-age=60"
|
||||||
|
set.headers["Vary"] = "search-params"
|
||||||
return { devices: rows }
|
return { devices: rows }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user