fix(migration): Ref 전용 아이콘을 업로드 API로 이관한다

전용 아이콘 바이트를 모두 검증한 뒤 sam-image 서명 API에 결정적 경로로 등록한다. API 반환 경로를 계정과 아이콘 소유 목록에 연결하고 Core에서 바뀐 현재 선택은 보존한다.
This commit is contained in:
2026-08-24 08:33:19 +00:00
parent 1e79611089
commit 0c5dee1af8
14 changed files with 891 additions and 29 deletions
+26 -1
View File
@@ -3,7 +3,13 @@ import type { Pool as PgPool, PoolClient, QueryResult } from 'pg';
import { describe, expect, it, vi } from 'vitest';
import { upsertRows } from '../src/db.js';
import { mapMember, MEMBER_PRESERVED_COLUMNS, migrateGateway, preflightMemberConflicts } from '../src/gateway.js';
import {
mapMember,
MEMBER_PRESERVED_COLUMNS,
migrateGateway,
normalizeLegacyIconPicture,
preflightMemberConflicts,
} from '../src/gateway.js';
const memberRow = (overrides: Record<string, unknown> = {}) => ({
NO: 7,
@@ -44,6 +50,25 @@ describe('legacy gateway member migration', () => {
});
});
it('removes only the Ref cache marker while preserving the original icon metadata', () => {
const mapped = mapMember(
memberRow({ PICTURE: 'users/core/' + 'a'.repeat(32) + '.png?=20260809', IMGSVR: 0 }),
new Date('2026-08-17T00:00:00.000Z'),
null
);
expect(normalizeLegacyIconPicture('legacy.png?=20260809')).toBe('legacy.png');
expect(normalizeLegacyIconPicture('literal.png?other')).toBe('literal.png?other');
expect(mapped).toMatchObject({
picture: 'users/core/' + 'a'.repeat(32) + '.png',
image_server: 0,
});
expect((mapped.legacy_data as { value: unknown }).value).toMatchObject({
picture: 'users/core/' + 'a'.repeat(32) + '.png?=20260809',
imageServer: 0,
});
});
it('preserves target-owned credentials and OAuth state on a repeated member upsert', async () => {
const query = vi.fn(async (_sql: string, _values?: unknown[]) => ({ rows: [], rowCount: 0 }));
const client = { query } as unknown as PoolClient;