@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
358 lines (300 loc) • 13.8 kB
text/typescript
import { describe, expect, it } from 'vitest';
import {
COMPOUND_STYLES,
STYLE_ADJECTIVE_PATTERNS,
STYLE_KEYWORDS,
STYLE_SYNONYMS,
extractStyleAdjectives,
getAllStyleKeywords,
getCompoundStyles,
isStyleAdjective,
normalizeStyleTerm,
} from '@/server/services/comfyui/config/promptToolConst';
describe('promptToolConst', () => {
describe('STYLE_KEYWORDS', () => {
it('should have all expected categories', () => {
const expectedCategories = [
'ARTISTS',
'ART_STYLES',
'LIGHTING',
'PHOTOGRAPHY',
'QUALITY',
'RENDERING',
'COLOR_MOOD',
'TEXTURE_MATERIAL',
];
expect(Object.keys(STYLE_KEYWORDS)).toEqual(expectedCategories);
});
it('should have expanded keywords in each category', () => {
// Minimum expectations to allow expansion
expect(STYLE_KEYWORDS.ARTISTS.length).toBeGreaterThanOrEqual(20);
expect(STYLE_KEYWORDS.ART_STYLES.length).toBeGreaterThanOrEqual(52);
expect(STYLE_KEYWORDS.LIGHTING.length).toBeGreaterThanOrEqual(37);
expect(STYLE_KEYWORDS.PHOTOGRAPHY.length).toBeGreaterThanOrEqual(49);
expect(STYLE_KEYWORDS.QUALITY.length).toBeGreaterThanOrEqual(39);
expect(STYLE_KEYWORDS.RENDERING.length).toBeGreaterThanOrEqual(39);
expect(STYLE_KEYWORDS.COLOR_MOOD.length).toBeGreaterThanOrEqual(56);
expect(STYLE_KEYWORDS.TEXTURE_MATERIAL.length).toBeGreaterThanOrEqual(60);
});
it('should not have duplicate keywords within categories', () => {
Object.entries(STYLE_KEYWORDS).forEach(([, keywords]: [string, readonly string[]]) => {
const uniqueKeywords = [...new Set(keywords)];
expect(keywords.length).toBe(uniqueKeywords.length);
});
});
it('should have lowercase keywords', () => {
Object.values(STYLE_KEYWORDS).forEach((keywords: readonly string[]) => {
keywords.forEach((keyword: string) => {
expect(keyword).toBe(keyword.toLowerCase());
});
});
});
});
describe('STYLE_SYNONYMS', () => {
it('should have synonym mappings', () => {
// Minimum number of synonym groups to allow expansion
expect(Object.keys(STYLE_SYNONYMS).length).toBeGreaterThanOrEqual(15);
expect(Object.keys(STYLE_SYNONYMS).length).toBeLessThanOrEqual(50); // Reasonable upper bound
});
it('should map common variations', () => {
expect(STYLE_SYNONYMS['photorealistic']).toContain('photo-realistic');
expect(STYLE_SYNONYMS['photorealistic']).toContain('photo realistic');
expect(STYLE_SYNONYMS['photorealistic']).toContain('lifelike');
expect(STYLE_SYNONYMS['4k']).toContain('4k resolution');
expect(STYLE_SYNONYMS['4k']).toContain('ultra hd');
expect(STYLE_SYNONYMS['cinematic']).toContain('filmic');
expect(STYLE_SYNONYMS['cinematic']).toContain('movie-like');
});
it('should have unique synonyms for each key', () => {
Object.entries(STYLE_SYNONYMS).forEach(([, synonyms]: [string, string[]]) => {
const uniqueSynonyms = [...new Set(synonyms)];
expect(synonyms.length).toBe(uniqueSynonyms.length);
});
});
it('should not have overlapping synonyms between different keys', () => {
const allSynonyms: string[] = [];
const duplicates: string[] = [];
Object.values(STYLE_SYNONYMS).forEach((synonyms: string[]) => {
synonyms.forEach((synonym: string) => {
if (allSynonyms.includes(synonym)) {
duplicates.push(synonym);
}
allSynonyms.push(synonym);
});
});
expect(duplicates).toEqual([]);
});
});
describe('COMPOUND_STYLES', () => {
it('should have compound style definitions', () => {
// Minimum range to allow expansion
expect(COMPOUND_STYLES.length).toBeGreaterThanOrEqual(35);
expect(COMPOUND_STYLES.length).toBeLessThanOrEqual(150); // Reasonable upper bound
});
it('should include expected compound styles', () => {
expect(COMPOUND_STYLES).toContain('studio ghibli style');
expect(COMPOUND_STYLES).toContain('cinematic lighting');
expect(COMPOUND_STYLES).toContain('dramatic lighting');
expect(COMPOUND_STYLES).toContain('depth of field');
expect(COMPOUND_STYLES).toContain('physically based rendering');
expect(COMPOUND_STYLES).toContain('global illumination');
});
it('should have unique compound styles', () => {
const uniqueStyles = [...new Set(COMPOUND_STYLES)];
expect(COMPOUND_STYLES.length).toBe(uniqueStyles.length);
});
it('should have lowercase compound styles', () => {
COMPOUND_STYLES.forEach((style: string) => {
expect(style).toBe(style.toLowerCase());
});
});
});
describe('STYLE_ADJECTIVE_PATTERNS', () => {
it('should have all expected pattern categories', () => {
const expectedPatterns = [
'quality',
'artistic',
'visual',
'mood',
'texture',
'scale',
'detail',
'professional',
];
expect(Object.keys(STYLE_ADJECTIVE_PATTERNS)).toEqual(expectedPatterns);
});
it('should match expected adjectives', () => {
// Quality patterns
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('sharp')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('blurry')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('crisp')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('walking')).toBe(false);
// Artistic patterns
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('abstract')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('surreal')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('minimal')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('minimalist')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('running')).toBe(false);
// Visual patterns
expect(STYLE_ADJECTIVE_PATTERNS.visual.test('bright')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.visual.test('dark')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.visual.test('vibrant')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.visual.test('opened')).toBe(false);
// Mood patterns
expect(STYLE_ADJECTIVE_PATTERNS.mood.test('dramatic')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.mood.test('peaceful')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.mood.test('mysterious')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.mood.test('walking')).toBe(false);
});
it('should be case insensitive', () => {
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('Sharp')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.quality.test('SHARP')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('Abstract')).toBe(true);
expect(STYLE_ADJECTIVE_PATTERNS.artistic.test('ABSTRACT')).toBe(true);
});
});
describe('getAllStyleKeywords', () => {
it('should return flattened array of all keywords', () => {
const allKeywords = getAllStyleKeywords();
expect(Array.isArray(allKeywords)).toBe(true);
// Minimum range for total keywords to allow expansion
expect(allKeywords.length).toBeGreaterThanOrEqual(350);
expect(allKeywords.length).toBeLessThanOrEqual(750);
// Check that it contains keywords from different categories
expect(allKeywords).toContain('by greg rutkowski');
expect(allKeywords).toContain('photorealistic');
expect(allKeywords).toContain('dramatic lighting');
expect(allKeywords).toContain('bokeh');
expect(allKeywords).toContain('masterpiece');
});
it('should return readonly array', () => {
const keywords = getAllStyleKeywords();
// TypeScript will enforce readonly at compile time
expect(Object.isFrozen(keywords) || Array.isArray(keywords)).toBe(true);
});
});
describe('getCompoundStyles', () => {
it('should return compound styles array', () => {
const compounds = getCompoundStyles();
expect(Array.isArray(compounds)).toBe(true);
// Minimum range to allow expansion
expect(compounds.length).toBeGreaterThanOrEqual(35);
expect(compounds.length).toBeLessThanOrEqual(150);
expect(compounds).toContain('studio ghibli style');
expect(compounds).toContain('cinematic lighting');
});
it('should return the same array as COMPOUND_STYLES', () => {
const compounds = getCompoundStyles();
expect(compounds).toEqual(COMPOUND_STYLES);
});
});
describe('normalizeStyleTerm', () => {
it('should normalize known synonyms', () => {
expect(normalizeStyleTerm('photo-realistic')).toBe('photorealistic');
expect(normalizeStyleTerm('photo realistic')).toBe('photorealistic');
expect(normalizeStyleTerm('lifelike')).toBe('photorealistic');
expect(normalizeStyleTerm('4k resolution')).toBe('4k');
expect(normalizeStyleTerm('ultra hd')).toBe('4k');
expect(normalizeStyleTerm('filmic')).toBe('cinematic');
expect(normalizeStyleTerm('movie-like')).toBe('cinematic');
});
it('should return original term if not a synonym', () => {
expect(normalizeStyleTerm('unknown-term')).toBe('unknown-term');
expect(normalizeStyleTerm('random')).toBe('random');
expect(normalizeStyleTerm('test')).toBe('test');
});
it('should handle case insensitive matching', () => {
expect(normalizeStyleTerm('Photo-Realistic')).toBe('photorealistic');
expect(normalizeStyleTerm('PHOTO REALISTIC')).toBe('photorealistic');
expect(normalizeStyleTerm('Filmic')).toBe('cinematic');
});
it('should handle empty or invalid input', () => {
expect(normalizeStyleTerm('')).toBe('');
expect(normalizeStyleTerm(' ')).toBe(' ');
});
});
describe('isStyleAdjective', () => {
it('should identify style adjectives', () => {
// Quality adjectives
expect(isStyleAdjective('sharp')).toBe(true);
expect(isStyleAdjective('blurry')).toBe(true);
expect(isStyleAdjective('crisp')).toBe(true);
// Artistic adjectives
expect(isStyleAdjective('abstract')).toBe(true);
expect(isStyleAdjective('surreal')).toBe(true);
expect(isStyleAdjective('minimal')).toBe(true);
// Visual adjectives
expect(isStyleAdjective('bright')).toBe(true);
expect(isStyleAdjective('dark')).toBe(true);
expect(isStyleAdjective('vibrant')).toBe(true);
// Mood adjectives
expect(isStyleAdjective('dramatic')).toBe(true);
expect(isStyleAdjective('peaceful')).toBe(true);
expect(isStyleAdjective('mysterious')).toBe(true);
});
it('should reject non-style adjectives', () => {
expect(isStyleAdjective('walking')).toBe(false);
expect(isStyleAdjective('running')).toBe(false);
expect(isStyleAdjective('opened')).toBe(false);
expect(isStyleAdjective('closed')).toBe(false);
expect(isStyleAdjective('basic')).toBe(false);
expect(isStyleAdjective('normal')).toBe(false);
});
it('should handle case insensitive matching', () => {
expect(isStyleAdjective('Sharp')).toBe(true);
expect(isStyleAdjective('SHARP')).toBe(true);
expect(isStyleAdjective('Abstract')).toBe(true);
expect(isStyleAdjective('ABSTRACT')).toBe(true);
});
});
describe('extractStyleAdjectives', () => {
it('should extract style adjectives from word array', () => {
const words = ['a', 'sharp', 'walking', 'robot', 'with', 'dramatic', 'lighting'];
const adjectives = extractStyleAdjectives(words);
expect(adjectives).toEqual(['sharp', 'dramatic']);
});
it('should handle empty array', () => {
expect(extractStyleAdjectives([])).toEqual([]);
});
it('should handle array with no style adjectives', () => {
const words = ['walking', 'running', 'jumping', 'swimming'];
expect(extractStyleAdjectives(words)).toEqual([]);
});
it('should handle array with all style adjectives', () => {
const words = ['sharp', 'bright', 'dramatic', 'mysterious'];
expect(extractStyleAdjectives(words)).toEqual(words);
});
it('should preserve original case', () => {
const words = ['Sharp', 'BRIGHT', 'Dramatic'];
const adjectives = extractStyleAdjectives(words);
expect(adjectives).toEqual(['Sharp', 'BRIGHT', 'Dramatic']);
});
});
describe('Integration tests', () => {
it('should have consistent data across all exports', () => {
const allKeywords = getAllStyleKeywords();
const totalInCategories = Object.values(STYLE_KEYWORDS).reduce(
(sum: number, keywords: readonly string[]) => sum + keywords.length,
0,
);
expect(allKeywords.length).toBe(totalInCategories);
});
it('should not have keywords that are also synonyms', () => {
const allKeywords = getAllStyleKeywords();
const allSynonyms = new Set(Object.values(STYLE_SYNONYMS).flat());
const overlap = allKeywords.filter((keyword: string) => allSynonyms.has(keyword));
// Allow reasonable range of overlaps
expect(overlap.length).toBeGreaterThanOrEqual(10);
expect(overlap.length).toBeLessThanOrEqual(20); // Reasonable overlap range
});
it('should have compound styles that contain style keywords', () => {
const compounds = getCompoundStyles();
const keywords = getAllStyleKeywords();
// At least some compound styles should contain individual keywords
const compoundsWithKeywords = compounds.filter((compound: string) => {
return keywords.some((keyword: string) => compound.includes(keyword));
});
expect(compoundsWithKeywords.length).toBeGreaterThan(0);
});
});
});