import { describe, expect, it, vi } from 'vitest'; import { createWatermarkTemplateRequestSchema } from '@atlas/contracts'; import { WatermarksService } from '../src/watermarks/watermarks.service.js'; const templateId = '46ea1f9c-28d0-4b32-92e1-3d4e74ad7757'; const templateRow = (x: number, y: number, position: string) => ({ id: templateId, name: '测试水印', kind: 'text' as const, current_version: 1, created_at: new Date('2026-09-25T00:00:00Z'), updated_at: new Date('2026-09-25T00:00:00Z'), revision: 1, text_content: 'Atlas', image_asset_id: null, font_code: 'NotoSans', font_size_px: 15, opacity: '0.2600', scale_ratio: null, position, position_x: x.toFixed(4), position_y: y.toFixed(4), }); describe('水印任意位置坐标', () => { it('契约接收精确坐标、兼容旧位置,并拒绝创建时只给一个坐标', () => { const base = { name: '测试水印', kind: 'text', config: { text_content: 'Atlas', font_code: 'NotoSans', font_size_px: 15, opacity: '0.26', }, }; expect(createWatermarkTemplateRequestSchema.safeParse({ ...base, config: { ...base.config, position_x: 0.3737, position_y: 0.6161 }, }).success).toBe(true); expect(createWatermarkTemplateRequestSchema.safeParse({ ...base, config: { ...base.config, position: 'bottom_right' }, }).success).toBe(true); expect(createWatermarkTemplateRequestSchema.safeParse({ ...base, config: { ...base.config, position_x: 0.3737 }, }).success).toBe(false); }); it('创建时保存精确坐标并在读取响应中返回,不吸附九宫格', async () => { let insertedVersion: unknown[] = []; const query = vi.fn(async (sql: string, args?: unknown[]) => { if (sql.includes('SELECT code FROM atlas.watermark_fonts')) return { rows: [{ code: 'NotoSans' }] }; if (sql.includes('INSERT INTO atlas.watermark_templates')) return { rows: [{ id: templateId }] }; if (sql.includes('INSERT INTO atlas.watermark_template_versions')) { insertedVersion = args ?? []; return { rows: [] }; } if (sql.includes('SELECT template.id')) return { rows: [templateRow( Number(insertedVersion[9]), Number(insertedVersion[10]), String(insertedVersion[11]), )] }; throw new Error(`未知 SQL: ${sql}`); }); const run = vi.fn(async (_input: unknown, work: (tx: { query: typeof query }) => Promise) => work({ query })); const service = new WatermarksService( { run } as unknown as ConstructorParameters[0], {} as ConstructorParameters[1], ); const result = await service.create('workspace', 'user', 'key', { name: '测试水印', kind: 'text', config: { text_content: 'Atlas', font_code: 'NotoSans', font_size_px: 15, opacity: '0.26', position_x: 0.3737, position_y: 0.6161, }, }); expect(insertedVersion.slice(9, 12)).toEqual([0.3737, 0.6161, 'center']); expect(result).toMatchObject({ body: { data: { config: { position_x: 0.3737, position_y: 0.6161 } } }, }); }); it('旧版九宫格输入换算为精确坐标', async () => { let insertedVersion: unknown[] = []; const query = vi.fn(async (sql: string, args?: unknown[]) => { if (sql.includes('SELECT code FROM atlas.watermark_fonts')) return { rows: [{ code: 'NotoSans' }] }; if (sql.includes('INSERT INTO atlas.watermark_templates')) return { rows: [{ id: templateId }] }; if (sql.includes('INSERT INTO atlas.watermark_template_versions')) { insertedVersion = args ?? []; return { rows: [] }; } if (sql.includes('SELECT template.id')) return { rows: [templateRow(1, 0, 'top_right')] }; throw new Error(`未知 SQL: ${sql}`); }); const run = vi.fn(async (_input: unknown, work: (tx: { query: typeof query }) => Promise) => work({ query })); const service = new WatermarksService( { run } as unknown as ConstructorParameters[0], {} as ConstructorParameters[1], ); await service.create('workspace', 'user', 'key', { name: '测试水印', kind: 'text', config: { text_content: 'Atlas', font_code: 'NotoSans', font_size_px: 15, opacity: '0.26', position: 'top_right', }, }); expect(insertedVersion.slice(9, 12)).toEqual([1, 0, 'top_right']); }); });