mirror of
https://github.com/jcwimer/wrestlingApp
synced 2026-05-22 13:14:45 +00:00
161 lines
5.1 KiB
JavaScript
161 lines
5.1 KiB
JavaScript
import { describe, expect, it, vi } from "vitest"
|
|
import {
|
|
cleanupExpiredLocalStorage,
|
|
loadJson,
|
|
MATCH_DATA_TTL_MS,
|
|
performIfChanged,
|
|
removeKey,
|
|
saveJson,
|
|
SHORT_LIVED_TTL_MS
|
|
} from "match-state-transport"
|
|
|
|
describe("match state transport", () => {
|
|
it("loads saved json from storage", () => {
|
|
const storage = {
|
|
getItem: vi.fn(() => '{"score":3}')
|
|
}
|
|
|
|
expect(loadJson(storage, "match-state:1:1001")).toEqual({ score: 3 })
|
|
expect(storage.getItem).toHaveBeenCalledWith("match-state:1:1001")
|
|
})
|
|
|
|
it("returns null when stored json is invalid", () => {
|
|
const storage = {
|
|
getItem: vi.fn(() => "{not-json")
|
|
}
|
|
|
|
expect(loadJson(storage, "bad")).toBe(null)
|
|
})
|
|
|
|
it("saves and removes json values in storage", () => {
|
|
const storage = {
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
expect(saveJson(storage, "key", { score: 5 })).toBe(true)
|
|
expect(storage.setItem).toHaveBeenCalledWith("key", '{"score":5}')
|
|
|
|
expect(removeKey(storage, "key")).toBe(true)
|
|
expect(storage.removeItem).toHaveBeenCalledWith("key")
|
|
})
|
|
|
|
it("saves and loads expiring json values", () => {
|
|
vi.spyOn(Date, "now").mockReturnValue(1_000)
|
|
const storage = {
|
|
getItem: vi.fn(),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
expect(saveJson(storage, "match-state:1:1001", { score: 5 }, { ttlMs: MATCH_DATA_TTL_MS })).toBe(true)
|
|
const savedValue = JSON.parse(storage.setItem.mock.calls[0][1])
|
|
expect(savedValue.expiresAt).toBe(1_000 + MATCH_DATA_TTL_MS)
|
|
|
|
storage.getItem.mockReturnValue(JSON.stringify(savedValue))
|
|
expect(loadJson(storage, "match-state:1:1001")).toEqual({ score: 5 })
|
|
})
|
|
|
|
it("removes expired json values when loading", () => {
|
|
vi.spyOn(Date, "now").mockReturnValue(10_000)
|
|
const storage = {
|
|
getItem: vi.fn(() => JSON.stringify({
|
|
__wrestlingAppStorage: true,
|
|
expiresAt: 9_000,
|
|
value: { score: 5 }
|
|
})),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
expect(loadJson(storage, "match-state:1:1001")).toBe(null)
|
|
expect(storage.removeItem).toHaveBeenCalledWith("match-state:1:1001")
|
|
})
|
|
|
|
it("cleans expired app localStorage keys and leaves unrelated keys alone", () => {
|
|
const values = {
|
|
"match-state:1:1001": JSON.stringify({
|
|
__wrestlingAppStorage: true,
|
|
expiresAt: 1_000,
|
|
value: { score: 5 }
|
|
}),
|
|
"mat-selected-bout:1:2": JSON.stringify({
|
|
__wrestlingAppStorage: true,
|
|
expiresAt: 50_000,
|
|
value: { boutNumber: 1001 }
|
|
}),
|
|
"unrelated": JSON.stringify({
|
|
__wrestlingAppStorage: true,
|
|
expiresAt: 1_000,
|
|
value: "keep"
|
|
})
|
|
}
|
|
const keys = Object.keys(values)
|
|
const storage = {
|
|
length: keys.length,
|
|
key: vi.fn((index) => keys[index]),
|
|
getItem: vi.fn((key) => values[key]),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
cleanupExpiredLocalStorage(storage, 10_000)
|
|
|
|
expect(storage.removeItem).toHaveBeenCalledWith("match-state:1:1001")
|
|
expect(storage.removeItem).not.toHaveBeenCalledWith("mat-selected-bout:1:2")
|
|
expect(storage.removeItem).not.toHaveBeenCalledWith("unrelated")
|
|
})
|
|
|
|
it("removes stale legacy stat data using its updated timestamp", () => {
|
|
const keys = ["w1-8-1001"]
|
|
const storage = {
|
|
length: 1,
|
|
key: vi.fn((index) => keys[index]),
|
|
getItem: vi.fn(() => JSON.stringify({
|
|
stats: "old",
|
|
updated_at: "2026-04-01T00:00:00.000Z"
|
|
})),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
cleanupExpiredLocalStorage(storage, Date.parse("2026-04-13T00:00:00.000Z"))
|
|
|
|
expect(storage.removeItem).toHaveBeenCalledWith("w1-8-1001")
|
|
})
|
|
|
|
it("adds expiration to legacy app values that are still valid", () => {
|
|
const keys = ["mat-last-match-result:8:3"]
|
|
const storage = {
|
|
length: 1,
|
|
key: vi.fn((index) => keys[index]),
|
|
getItem: vi.fn(() => JSON.stringify("Result text")),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn()
|
|
}
|
|
|
|
cleanupExpiredLocalStorage(storage, 2_000)
|
|
|
|
const wrappedValue = JSON.parse(storage.setItem.mock.calls[0][1])
|
|
expect(storage.setItem.mock.calls[0][0]).toBe("mat-last-match-result:8:3")
|
|
expect(wrappedValue.value).toBe("Result text")
|
|
expect(wrappedValue.expiresAt).toBe(2_000 + SHORT_LIVED_TTL_MS)
|
|
})
|
|
|
|
it("only performs subscription actions when the payload changes", () => {
|
|
const subscription = {
|
|
perform: vi.fn()
|
|
}
|
|
|
|
const firstSerialized = performIfChanged(subscription, "send_stat", { new_w1_stat: "T3" }, null)
|
|
const secondSerialized = performIfChanged(subscription, "send_stat", { new_w1_stat: "T3" }, firstSerialized)
|
|
const thirdSerialized = performIfChanged(subscription, "send_stat", { new_w1_stat: "T3 E1" }, secondSerialized)
|
|
|
|
expect(subscription.perform).toHaveBeenCalledTimes(2)
|
|
expect(subscription.perform).toHaveBeenNthCalledWith(1, "send_stat", { new_w1_stat: "T3" })
|
|
expect(subscription.perform).toHaveBeenNthCalledWith(2, "send_stat", { new_w1_stat: "T3 E1" })
|
|
expect(firstSerialized).toBe('{"new_w1_stat":"T3"}')
|
|
expect(secondSerialized).toBe(firstSerialized)
|
|
expect(thirdSerialized).toBe('{"new_w1_stat":"T3 E1"}')
|
|
})
|
|
})
|