Layered: routes -> services -> repositories -> db, over PostgreSQL 18 with split roles (findr_migrator for DDL, findr_app for DML). Keycloak JWT auth with per-endpoint scope guards. Drizzle migrations run with the DDL role at container start. Pick-by-light: one ESP32 controller drives one WS2812 chain through several boxes; each box owns a contiguous LED slice. MQTT contract in src/lib/mqtt-topics.ts / docs/mqtt.md. /v1/pick controls light/idle/off and /v1/boxes + /v1/controllers manage the strip mapping. Reference resource /v1/parts wired end to end. Vitest, Biome, multi-stage Dockerfile, local compose.yml with Postgres. Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { columnIndex, gridLedIndex, ledIndexFor } from "../src/lib/led-map.js";
|
|
|
|
describe("led-map", () => {
|
|
it("maps column letters to indices", () => {
|
|
expect(columnIndex("A")).toBe(0);
|
|
expect(columnIndex("d")).toBe(3);
|
|
});
|
|
|
|
it("progressive wiring is row-major", () => {
|
|
const grid = { columns: 8, rows: 5, wiring: "progressive" as const };
|
|
expect(gridLedIndex("A", 1, grid)).toBe(0);
|
|
expect(gridLedIndex("D", 1, grid)).toBe(3);
|
|
expect(gridLedIndex("A", 2, grid)).toBe(8);
|
|
expect(gridLedIndex("D", 4, grid)).toBe(27);
|
|
});
|
|
|
|
it("serpentine wiring reverses every other row", () => {
|
|
const grid = { columns: 8, rows: 5, wiring: "serpentine" as const };
|
|
expect(gridLedIndex("A", 1, grid)).toBe(0); // row 1: L→R
|
|
expect(gridLedIndex("A", 2, grid)).toBe(15); // row 2: R→L, so col A is last
|
|
expect(gridLedIndex("H", 2, grid)).toBe(8);
|
|
});
|
|
|
|
it("an explicit ledIndex overrides the grid", () => {
|
|
const grid = { columns: 8, rows: 5, wiring: "progressive" as const };
|
|
expect(ledIndexFor({ column: "A", row: 1, ledIndex: 99 }, grid)).toBe(99);
|
|
expect(ledIndexFor({ column: "A", row: 1, ledIndex: null }, grid)).toBe(0);
|
|
});
|
|
});
|