Files
QingTing-Player/test/metadata_stability_test.dart
T
lxh2875931338 b8138ad125 feat(metadata): 元数据引擎第 3 步之一——一致性与冲突感知的状态机
之前只要 confidence>=0.6 且 validation_count>=2 就升级 verified,
两次读到不同结果也会被当成"验证通过",错误标签一旦写入很难纠正。

新增 MetadataStability(纯函数,可单测):
- 首次见到 → pending(first_seen),次数 1
- 与上次记录标题/艺术家冲突 → pending(conflict),次数重置为 1
- 一致且次数 >= 2、置信度达标 → verified(stable)
- 置信度不足 → pending(low_confidence),次数继续累计
- 字段缺失不算冲突;比较时忽略大小写与首尾空格

metadata_service 改为调用该状态机,并打印判定原因:
  🧭 [MetadataService] 状态机: stable → verified(validations=2)

测试:新增 test/metadata_stability_test.dart 7 个用例,
连同原有 9 个共 16 个,flutter test 全绿。
已构建并安装 release 验证。
2026-10-24 20:24:55 +08:00

125 lines
3.5 KiB
Dart

// test/metadata_stability_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:qingting_player/metadata/metadata_model.dart';
import 'package:qingting_player/metadata/metadata_stability.dart';
void main() {
CandidateMetadata candidate({double confidence = 0.8}) => CandidateMetadata(
metadata: const NormalizedMetadata(title: '夜曲', artist: '周杰伦'),
confidence: confidence,
);
group('MetadataStability', () {
test('首次见到 → pending,次数 1', () {
final d = MetadataStability.decide(
previous: null,
candidate: candidate(),
title: '夜曲',
artist: '周杰伦',
);
expect(d.status, 'pending');
expect(d.reason, 'first_seen');
expect(d.validationCount, 1);
});
test('第二次结果一致且置信度达标 → verified', () {
final d = MetadataStability.decide(
previous: {
'title': '夜曲',
'artist': '周杰伦',
'confidence': 0.8,
'validation_count': 1,
},
candidate: candidate(),
title: '夜曲',
artist: '周杰伦',
);
expect(d.status, 'verified');
expect(d.reason, 'stable');
expect(d.validationCount, 2);
});
test('标题或艺术家冲突 → 次数重置为 1,保持 pending', () {
final d = MetadataStability.decide(
previous: {
'title': '夜曲',
'artist': '周杰伦',
'confidence': 0.9,
'validation_count': 5,
},
candidate: candidate(),
title: '完全不同的歌',
artist: '别的歌手',
);
expect(d.status, 'pending');
expect(d.reason, 'conflict');
expect(d.validationCount, 1);
});
test('置信度不足 → pending,但次数继续累计', () {
final d = MetadataStability.decide(
previous: {
'title': '夜曲',
'artist': '周杰伦',
'confidence': 0.4,
'validation_count': 3,
},
candidate: candidate(confidence: 0.5),
title: '夜曲',
artist: '周杰伦',
);
expect(d.status, 'pending');
expect(d.reason, 'low_confidence');
expect(d.validationCount, 4);
});
test('一致但只验证过一次 → pending', () {
final d = MetadataStability.decide(
previous: {
'title': '夜曲',
'artist': '周杰伦',
'confidence': 0.8,
'validation_count': 0,
},
candidate: candidate(),
title: '夜曲',
artist: '周杰伦',
);
expect(d.status, 'pending');
expect(d.reason, 'need_second_validation');
expect(d.validationCount, 1);
});
test('字段缺失不算冲突(旧记录没有艺术家)', () {
final d = MetadataStability.decide(
previous: {
'title': '夜曲',
'artist': '',
'confidence': 0.7,
'validation_count': 1,
},
candidate: candidate(),
title: '夜曲',
artist: '周杰伦',
);
expect(d.status, 'verified');
expect(d.validationCount, 2);
});
test('大小写与首尾空格不影响一致性判断', () {
final d = MetadataStability.decide(
previous: {
'title': ' Night Song ',
'artist': 'JAY',
'confidence': 0.8,
'validation_count': 1,
},
candidate: candidate(),
title: 'night song',
artist: 'jay',
);
expect(d.status, 'verified');
});
});
}