import 'package:flutter_test/flutter_test.dart'; import 'package:life_helper/domain/frame/validate_frame_level.dart'; import 'package:life_helper/domain/models/frame_pattern.dart'; import 'package:life_helper/domain/models/habit.dart'; final _patterns = [ const FramePatternModel( id: 'fp_alcohol', domain: 'drink', avoidanceKeyword: '술 끊기', l0Example: '술 끊기', l1SimpleReplace: '음주 중단', l2Suggestion: '저녁엔 무알콜 음료 마시기', l3Identity: '나는 맑은 정신을 우선시하는 사람이다', ), const FramePatternModel( id: 'fp_general', domain: 'general', avoidanceKeyword: '안 하기', l0Example: '안 하기', l2Suggestion: '대신 다른 행동 정의하기', ), ]; void main() { test('empty framed text → reject', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l2, framedText: ''), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.reject); }); test('L0 → reject + suggestions', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l0, framedText: '술 끊기'), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.reject); expect(r.suggestions, isNotEmpty); expect(r.suggestions.any((s) => s.level == FrameLevel.l2), true); }); test('L1 → reject + suggestions even when not matching any keyword', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l1, framedText: '담배 줄이기'), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.reject); // Should fall back to 'general' domain suggestion. expect(r.suggestions, isNotEmpty); }); test('L2 clean → accept', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l2, framedText: '저녁엔 무알콜 음료 마시기'), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.accept); expect(r.suggestions, isEmpty); }); test('L2 with embedded avoidance keyword → warn', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l2, framedText: '술 끊기로 다짐'), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.warn); expect(r.avoidanceHits, isNotEmpty); expect(r.avoidanceHits.first.keyword, '술 끊기'); }); test('L3 identity frame, clean → accept', () { final r = validateFrameLevel( const FrameInput(level: FrameLevel.l3, framedText: '나는 맑은 정신을 우선시한다'), knownPatterns: _patterns, ); expect(r.status, FrameValidationStatus.accept); }); test('detectAvoidanceKeywords finds multiple occurrences', () { final hits = detectAvoidanceKeywords('술 끊기 / 다시 술 끊기 / 끝', _patterns); expect(hits.length, 2); }); }