import { describe, expect, test } from 'bun:test'; import { extractJson, coerceLabel } from '../src/classify-parse.js'; describe('extractJson', () => { test('pulls a JSON object out of a fenced/chatty reply', () => { const reply = 'Here you go:\n```json\n{"category":"glamour","thumbnailFitness":0.9}\n```\nDone.'; expect(extractJson(reply)).toEqual({ category: 'glamour', thumbnailFitness: 0.9 }); }); test('handles nested braces', () => { expect(extractJson('{"a":{"b":1},"c":2} trailing')).toEqual({ a: { b: 1 }, c: 2 }); }); test('returns null when there is no object', () => { expect(extractJson('no json here')).toBeNull(); expect(extractJson('{ broken')).toBeNull(); }); }); describe('coerceLabel', () => { test('accepts a valid label', () => { const l = coerceLabel('photo-1', { category: 'headshot', thumbnailFitness: 0.8, faceVisible: true, note: 'close crop', }); expect(l).toEqual({ photoId: 'photo-1', category: 'headshot', thumbnailFitness: 0.8, faceVisible: true, note: 'close crop', }); }); test('falls back to portrait on an unknown category', () => { expect(coerceLabel('p', { category: 'banana' }).category).toBe('portrait'); }); test('unwraps a one-element array category (model quirk)', () => { expect(coerceLabel('p', { category: ['lifestyle'] }).category).toBe('lifestyle'); }); test('clamps fitness to 0..1 and defaults non-numbers to 0', () => { expect(coerceLabel('p', { thumbnailFitness: 5 }).thumbnailFitness).toBe(1); expect(coerceLabel('p', { thumbnailFitness: -2 }).thumbnailFitness).toBe(0); expect(coerceLabel('p', { thumbnailFitness: 'x' }).thumbnailFitness).toBe(0); }); test('coerces faceVisible and note types', () => { const l = coerceLabel('p', { faceVisible: 1, note: 42 }); expect(l.faceVisible).toBe(true); expect(l.note).toBe(''); }); });