feat: tests and touchups

This commit is contained in:
zk
2026-03-26 00:34:40 +00:00
parent d3cc3d55fb
commit 3448f34e30
5 changed files with 321 additions and 2 deletions
+4 -1
View File
@@ -31,4 +31,7 @@ I_HAVE_BOOTSTRAPPED_OK
# new bootstrap flag
BOOTSTRAP_OK
# v3 bootstrap flag (omg)
_SETUP_OK
_SETUP_OK
# Local debugging: `just db-schema` (see Justfile-db)
db/schema.sql
+5 -1
View File
@@ -42,4 +42,8 @@ server-db-test-template-reset:
# Load the seeds into the database.
db-load-seeds POSTGRES_DB=DEFAULT_DB:
cd typescript/server && POSTGRES_URL="{{POSTGRES_URL}}/{{POSTGRES_DB}}" \
bun run src/scripts/load-seeds-pg.ts
bun run src/scripts/load-seeds-pg.ts
# Reset templatedb from migrations, then dump schema and indexes for local debugging.
db-schema: (db-reset "templatedb")
pg_dump "{{POSTGRES_URL}}/templatedb" --schema-only --no-owner --no-acl -f db/schema.sql
@@ -0,0 +1,3 @@
-- One MYT card integration row per account (upsert target for UPDATE_MYT_CARD_INFO).
ALTER TABLE "priv_svc_myt_card_info"
ADD CONSTRAINT "priv_svc_myt_card_info_user_id_key" UNIQUE ("user_id");
@@ -0,0 +1,160 @@
import DB from "#services/pg/db";
import { seedUser } from "#test-utils/pg-fixtures";
import { beforeEach, describe, expect, it } from "vitest";
import { ACTION_DeleteMytCardInfo } from "./delete-myt-card-info";
import { ACTION_UpdateMytCardInfo } from "./update-myt-card-info";
const CODE_A = "00000000000000000001";
const CODE_B = "00000000000000000002";
const CODE_OTHER = "00000000000000009999";
async function getMytCardRow(userId: number) {
return DB.selectFrom("priv_svc_myt_card_info")
.selectAll()
.where("user_id", "=", userId)
.executeTakeFirst();
}
describe("ACTION_UpdateMytCardInfo", () => {
let userId: number;
let username: string;
beforeEach(async () => {
({ id: userId, username } = await seedUser({ username: "test_user" }));
});
it("returns {} on success", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
const result = await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_A });
expect(result).toEqual({});
});
it("inserts a row on first call", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_A });
const row = await getMytCardRow(userId);
expect(row).toMatchObject({ user_id: userId, card_access_code: CODE_A });
});
it("updates card_access_code on second call for the same user", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_A });
await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_B });
const row = await getMytCardRow(userId);
expect(row?.card_access_code).toBe(CODE_B);
const { count } = await DB.selectFrom("priv_svc_myt_card_info")
.select(DB.fn.countAll().as("count"))
.where("user_id", "=", userId)
.executeTakeFirstOrThrow();
expect(Number(count)).toBe(1);
});
it("does not modify another user's row", async () => {
const other = await seedUser({ username: "other_user", email: "other@example.com" });
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await DB.insertInto("priv_svc_myt_card_info")
.values({ user_id: other.id, card_access_code: CODE_OTHER })
.execute();
await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_A });
const otherRow = await getMytCardRow(other.id);
expect(otherRow?.card_access_code).toBe(CODE_OTHER);
});
it("writes a GOOD action row on success", async () => {
const taker = { ip: "10.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateMytCardInfo(taker, { cardAccessCode: CODE_A });
const action = await DB.selectFrom("action")
.selectAll()
.where("kind", "=", "UPDATE_MYT_CARD_INFO")
.executeTakeFirstOrThrow();
expect(action).toMatchObject({
kind: "UPDATE_MYT_CARD_INFO",
result: "GOOD",
ip: "10.0.0.1",
user_id: userId,
});
});
});
describe("ACTION_DeleteMytCardInfo", () => {
let userId: number;
let username: string;
beforeEach(async () => {
({ id: userId, username } = await seedUser({ username: "test_user" }));
});
it("returns {} when no row exists", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
const result = await ACTION_DeleteMytCardInfo(taker, {});
expect(result).toEqual({});
});
it("deletes the user's row when present", async () => {
await DB.insertInto("priv_svc_myt_card_info")
.values({ user_id: userId, card_access_code: CODE_A })
.execute();
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_DeleteMytCardInfo(taker, {});
expect(await getMytCardRow(userId)).toBeUndefined();
});
it("does not delete another user's row", async () => {
const other = await seedUser({ username: "other_user", email: "other@example.com" });
await DB.insertInto("priv_svc_myt_card_info")
.values({ user_id: userId, card_access_code: CODE_A })
.execute();
await DB.insertInto("priv_svc_myt_card_info")
.values({ user_id: other.id, card_access_code: CODE_OTHER })
.execute();
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_DeleteMytCardInfo(taker, {});
const otherRow = await getMytCardRow(other.id);
expect(otherRow?.card_access_code).toBe(CODE_OTHER);
});
it("writes a GOOD action row on success when a row was deleted", async () => {
await DB.insertInto("priv_svc_myt_card_info")
.values({ user_id: userId, card_access_code: CODE_A })
.execute();
const taker = { ip: "10.0.0.1", acct: { id: userId, username } };
await ACTION_DeleteMytCardInfo(taker, {});
const action = await DB.selectFrom("action")
.selectAll()
.where("kind", "=", "DELETE_MYT_CARD_INFO")
.executeTakeFirstOrThrow();
expect(action).toMatchObject({
kind: "DELETE_MYT_CARD_INFO",
result: "GOOD",
ip: "10.0.0.1",
user_id: userId,
});
});
});
@@ -0,0 +1,149 @@
import DB from "#services/pg/db";
import { seedUser } from "#test-utils/pg-fixtures";
import { beforeEach, describe, expect, it } from "vitest";
import { ACTION_UpdateUserSettings } from "./update-user-settings";
async function getSettings(userId: number) {
return DB.selectFrom("account_settings")
.selectAll()
.where("user_id", "=", userId)
.executeTakeFirstOrThrow();
}
describe("ACTION_UpdateUserSettings", () => {
let userId: number;
let username: string;
beforeEach(async () => {
({ id: userId, username } = await seedUser({
username: "test_user",
withSettings: true,
}));
});
it("throws 400 when no fields are provided", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await expect(ACTION_UpdateUserSettings(taker, {})).rejects.toMatchObject({ code: 400 });
});
it("writes a BAD action row when no fields are provided", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await expect(ACTION_UpdateUserSettings(taker, {})).rejects.toThrow();
const action = await DB.selectFrom("action")
.select("result")
.where("kind", "=", "UPDATE_USER_SETTINGS")
.executeTakeFirstOrThrow();
expect(action.result).toBe("BAD");
});
it("returns {} on success", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
const result = await ACTION_UpdateUserSettings(taker, { invisible: true });
expect(result).toEqual({});
});
it("persists invisible to pf_invisible", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { invisible: true });
const row = await getSettings(userId);
expect(row.pf_invisible).toBe(true);
expect(row.pf_developer_mode).toBe(false);
expect(row.pf_advanced_mode).toBe(false);
expect(row.pf_contentious_content).toBe(false);
expect(row.pf_deletable_scores).toBe(false);
});
it("persists developerMode to pf_developer_mode", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { developerMode: true });
const row = await getSettings(userId);
expect(row.pf_developer_mode).toBe(true);
expect(row.pf_invisible).toBe(false);
});
it("persists contentiousContent to pf_contentious_content", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { contentiousContent: true });
const row = await getSettings(userId);
expect(row.pf_contentious_content).toBe(true);
});
it("persists advancedMode to pf_advanced_mode", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { advancedMode: true });
const row = await getSettings(userId);
expect(row.pf_advanced_mode).toBe(true);
});
it("persists deletableScores to pf_deletable_scores", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { deletableScores: true });
const row = await getSettings(userId);
expect(row.pf_deletable_scores).toBe(true);
});
it("updates multiple fields in one call and leaves others unchanged", async () => {
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, {
invisible: true,
developerMode: true,
});
const row = await getSettings(userId);
expect(row.pf_invisible).toBe(true);
expect(row.pf_developer_mode).toBe(true);
expect(row.pf_advanced_mode).toBe(false);
expect(row.pf_contentious_content).toBe(false);
expect(row.pf_deletable_scores).toBe(false);
});
it("does not modify another user's account_settings", async () => {
const other = await seedUser({
username: "other_user",
email: "other@example.com",
withSettings: true,
});
const taker = { ip: "127.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { invisible: true });
const otherRow = await getSettings(other.id);
expect(otherRow.pf_invisible).toBe(false);
});
it("writes a GOOD action row on success", async () => {
const taker = { ip: "10.0.0.1", acct: { id: userId, username } };
await ACTION_UpdateUserSettings(taker, { invisible: true });
const action = await DB.selectFrom("action")
.selectAll()
.where("kind", "=", "UPDATE_USER_SETTINGS")
.executeTakeFirstOrThrow();
expect(action).toMatchObject({
kind: "UPDATE_USER_SETTINGS",
result: "GOOD",
ip: "10.0.0.1",
user_id: userId,
});
});
});