chore: stage all in-progress work before repo split
CI / test (push) Has been cancelled

Web app: new entities (Image, RenderedAsset, SharedImage, Token,
DeviceImageHistory), enums, repositories, controllers, message handlers,
migrations, tests, frontend upload/library/sticker UI, Vue components.

Firmware: EPD background screen binaries + gen scripts, setup_bg header.

Infra: ddev config, test bundle, gitignore coverage dir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:11:31 -04:00
parent dd0970ed7c
commit 4002ff9fbf
156 changed files with 27333 additions and 92 deletions
+158
View File
@@ -0,0 +1,158 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useDevicesStore } from '@/stores/devices'
import type { Device } from '@/types'
const makeDevice = (overrides: Partial<Device> = {}): Device => ({
id: 1,
mac: 'AA:BB:CC:DD:EE:FF',
name: 'Living Room',
orientation: 'landscape',
rotationIntervalMinutes: 60,
wakeHour: null,
timezone: 'America/Chicago',
uniquenessWindow: 30,
linkedAt: '2026-01-01T00:00:00Z',
lastSeenAt: null,
lockedImageId: null,
...overrides,
})
describe('devices store', () => {
beforeEach(() => {
vi.unstubAllGlobals()
vi.restoreAllMocks()
setActivePinia(createPinia())
})
afterEach(() => {
// unstubAllGlobals is handled in beforeEach so stubs don't leak
// even if a test throws before afterEach runs
})
// DS-01
it('fetchDevices success populates devices and clears loading', async () => {
const mockDevices = [makeDevice()]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockDevices),
}))
const store = useDevicesStore()
await store.fetchDevices()
expect(store.devices).toEqual(mockDevices)
expect(store.loading).toBe(false)
expect(store.error).toBeNull()
})
// DS-02
it('fetchDevices network error sets error state', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('Network failure')))
const store = useDevicesStore()
await store.fetchDevices()
expect(store.devices).toEqual([])
expect(store.loading).toBe(false)
expect(store.error).toBe('Network failure')
})
// DS-02b — non-ok response
it('fetchDevices non-ok response sets error state', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
const store = useDevicesStore()
await store.fetchDevices()
expect(store.error).toBe('Failed to load devices')
expect(store.loading).toBe(false)
})
// DS-03
it('updateDevice patches local array entry', async () => {
const original = makeDevice({ id: 1, name: 'Old Name' })
const updated = makeDevice({ id: 1, name: 'New Name' })
const store = useDevicesStore()
store.devices = [original]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(updated),
}))
const result = await store.updateDevice(1, { name: 'New Name' })
expect(result.name).toBe('New Name')
expect(store.devices[0].name).toBe('New Name')
})
// DS-03b — updateDevice throws on failure
it('updateDevice throws on non-ok response', async () => {
const store = useDevicesStore()
store.devices = [makeDevice()]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
await expect(store.updateDevice(1, { name: 'x' })).rejects.toThrow('Failed to update device')
})
// DS-04
it('lockImage sets lockedImageId on local device', async () => {
const device = makeDevice({ id: 1, lockedImageId: null })
const locked = makeDevice({ id: 1, lockedImageId: 42 })
const store = useDevicesStore()
store.devices = [device]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(locked),
}))
const result = await store.lockImage(1, 42)
expect(result.lockedImageId).toBe(42)
expect(store.devices[0].lockedImageId).toBe(42)
})
// DS-05
it('unlockImage clears lockedImageId', async () => {
const device = makeDevice({ id: 1, lockedImageId: 42 })
const unlocked = makeDevice({ id: 1, lockedImageId: null })
const store = useDevicesStore()
store.devices = [device]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(unlocked),
}))
const result = await store.unlockImage(1)
expect(result.lockedImageId).toBeNull()
expect(store.devices[0].lockedImageId).toBeNull()
})
// DS-05b — lockImage throws on failure
it('lockImage throws on non-ok response', async () => {
const store = useDevicesStore()
store.devices = [makeDevice()]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
await expect(store.lockImage(1, 42)).rejects.toThrow('Failed to lock image')
})
// DS-05c — unlockImage throws on failure
it('unlockImage throws on non-ok response', async () => {
const store = useDevicesStore()
store.devices = [makeDevice()]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
await expect(store.unlockImage(1)).rejects.toThrow('Failed to unlock')
})
})