검토한 회원 번호의 이미지가 계속 유효성 조건을 어길 때만 업로드에서 제외한다. 기존 Ref 경로가 현재 선택인 경우 기본 아이콘으로 정리하고 최신 Core 선택은 보존한다.
110 lines
5.1 KiB
TypeScript
110 lines
5.1 KiB
TypeScript
import { Pool } from 'pg';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { legacyUserId } from '../src/identity.js';
|
|
import { syncImportedUserIcons, syncRejectedUserIcons, type PreparedLegacyUserIcon } from '../src/legacyUserIcons.js';
|
|
|
|
const databaseUrl = process.env.LEGACY_ICON_TEST_DATABASE_URL;
|
|
|
|
const imported = (memberNo: number, sourcePicture: string, picture: string): PreparedLegacyUserIcon => ({
|
|
memberNo,
|
|
userId: legacyUserId(memberNo),
|
|
sourcePicture,
|
|
normalizedSourcePicture: sourcePicture.replace(/\?=[0-9]{8}$/u, ''),
|
|
sourceImageServer: 1,
|
|
picture,
|
|
imageServer: 0,
|
|
createdAt: new Date('2026-08-09T00:00:00.000Z'),
|
|
source: 'legacy-file',
|
|
sha256: 'a'.repeat(64),
|
|
});
|
|
|
|
describe.skipIf(!databaseUrl)('legacy user icon PostgreSQL synchronization', () => {
|
|
it('updates only an unchanged Ref selection and preserves newer Core state', async () => {
|
|
const pool = new Pool({ connectionString: databaseUrl });
|
|
const client = await pool.connect();
|
|
const icons = [
|
|
imported(700_001, 'first.png?=20260809', `users/core2026/${'1'.repeat(32)}.png`),
|
|
imported(700_002, 'second.png?=20260809', `users/core2026/${'2'.repeat(32)}.png`),
|
|
imported(700_003, 'third.png?=20260809', `users/core2026/${'3'.repeat(32)}.png`),
|
|
];
|
|
try {
|
|
await client.query('BEGIN');
|
|
for (const [index, icon] of icons.entries()) {
|
|
const currentPicture =
|
|
index === 0
|
|
? icon.sourcePicture
|
|
: index === 1
|
|
? `users/core2026/${'8'.repeat(32)}.png`
|
|
: 'default.jpg';
|
|
await client.query(
|
|
`INSERT INTO "app_user"
|
|
("id", "login_id", "display_name", "password_hash", "password_salt",
|
|
"updated_at", "picture", "image_server")
|
|
VALUES ($1, $2, $3, 'hash', 'salt', CURRENT_TIMESTAMP, $4, $5)`,
|
|
[icon.userId, `icon-test-${index}`, `아이콘테스트-${index}`, currentPicture, index === 0 ? 1 : 0]
|
|
);
|
|
}
|
|
|
|
await expect(syncImportedUserIcons(client, icons, new Date('2026-08-24T00:00:00Z'))).resolves.toEqual({
|
|
currentLinked: 1,
|
|
libraryInserted: 3,
|
|
libraryRetired: 1,
|
|
targetPreserved: 2,
|
|
});
|
|
const accounts = await client.query<{ id: string; picture: string; image_server: number }>(
|
|
`SELECT "id", "picture", "image_server" FROM "app_user"
|
|
WHERE "id" = ANY($1::text[]) ORDER BY "login_id"`,
|
|
[icons.map((icon) => icon.userId)]
|
|
);
|
|
expect(accounts.rows.map(({ picture, image_server: imageServer }) => ({ picture, imageServer }))).toEqual([
|
|
{ picture: icons[0]!.picture, imageServer: 0 },
|
|
{ picture: `users/core2026/${'8'.repeat(32)}.png`, imageServer: 0 },
|
|
{ picture: 'default.jpg', imageServer: 0 },
|
|
]);
|
|
const library = await client.query<{ picture: string; retired_at: Date | null }>(
|
|
`SELECT "picture", "retired_at" FROM "user_icon"
|
|
WHERE "user_id" = ANY($1::text[]) ORDER BY "picture"`,
|
|
[icons.map((icon) => icon.userId)]
|
|
);
|
|
expect(library.rows).toHaveLength(3);
|
|
expect(library.rows.filter((row) => row.retired_at !== null)).toHaveLength(1);
|
|
|
|
const rejectedUserId = legacyUserId(700_004);
|
|
await client.query(
|
|
`INSERT INTO "app_user"
|
|
("id", "login_id", "display_name", "password_hash", "password_salt",
|
|
"updated_at", "picture", "image_server")
|
|
VALUES ($1, 'icon-test-rejected', '아이콘테스트-제외', 'hash', 'salt',
|
|
CURRENT_TIMESTAMP, 'invalid.gif?=20260809', 1)`,
|
|
[rejectedUserId]
|
|
);
|
|
await expect(
|
|
syncRejectedUserIcons(
|
|
client,
|
|
[
|
|
{
|
|
memberNo: 700_004,
|
|
userId: rejectedUserId,
|
|
sourcePicture: 'invalid.gif?=20260809',
|
|
normalizedSourcePicture: 'invalid.gif',
|
|
sourceImageServer: 1,
|
|
reason: 'not square',
|
|
},
|
|
],
|
|
new Date('2026-08-24T00:00:00Z')
|
|
)
|
|
).resolves.toEqual({ currentReset: 1, targetPreserved: 0 });
|
|
const rejectedAccount = await client.query<{ picture: string; image_server: number }>(
|
|
`SELECT "picture", "image_server" FROM "app_user" WHERE "id" = $1`,
|
|
[rejectedUserId]
|
|
);
|
|
expect(rejectedAccount.rows[0]).toEqual({ picture: 'default.jpg', image_server: 0 });
|
|
} finally {
|
|
await client.query('ROLLBACK');
|
|
client.release();
|
|
await pool.end();
|
|
}
|
|
});
|
|
});
|