Its server, client, app and curator, and the tools that write its assets (maps, Anki fixtures, icons), as they are; paths are fixed in the commits that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
144 lines
4.8 KiB
Dart
144 lines
4.8 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:archive/archive.dart';
|
|
import 'package:halkyon_learn_curator/src/sources/krdict.dart';
|
|
import 'package:halkyon_learn_curator/src/sources/wiktionary.dart';
|
|
import 'package:halkyon_learn_curator/src/wordfreq.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
/// The fixture packed as KRDict packs its export: its first file.
|
|
List<int> export() => ZipEncoder().encodeBytes(
|
|
Archive()..add(
|
|
ArchiveFile.bytes(
|
|
'1_5000_20260919.json',
|
|
File('test/fixtures/krdict-sample.json').readAsBytesSync(),
|
|
),
|
|
),
|
|
);
|
|
|
|
Future<List<Map<String, Object?>>> converted({
|
|
WordFrequencies? frequencies,
|
|
WiktionaryKorean? korean,
|
|
}) async {
|
|
final (:version, :entries) = krdictExport(export());
|
|
return [
|
|
for (final line in await krdictDictionary(
|
|
entries,
|
|
sourceVersion: version,
|
|
frequencies: frequencies,
|
|
korean: korean,
|
|
).toList())
|
|
jsonDecode(line) as Map<String, Object?>,
|
|
];
|
|
}
|
|
|
|
test('an export of another shape is refused rather than written as a '
|
|
'dictionary of no words', () async {
|
|
for (final files in [
|
|
<ArchiveFile>[],
|
|
[ArchiveFile.bytes('1_5000_20260919.xml', utf8.encode('<xml/>'))],
|
|
[
|
|
ArchiveFile.bytes(
|
|
'1_5000_20260919.json',
|
|
utf8.encode(jsonEncode({'LexicalResource': <String, Object?>{}})),
|
|
),
|
|
],
|
|
]) {
|
|
final archive = Archive();
|
|
files.forEach(archive.add);
|
|
final zip = ZipEncoder().encodeBytes(archive);
|
|
final (version: _, :entries) = krdictExport(zip);
|
|
|
|
await expectLater(entries.toList(), throwsFormatException);
|
|
}
|
|
});
|
|
|
|
test("KRDict's words with their level, pronunciation where it differs from "
|
|
'the spelling, Hanja, forms and English senses', () async {
|
|
final [header, ...entries] = await converted();
|
|
|
|
expect(header['source'], 'krdict');
|
|
expect(header['language'], 'ko');
|
|
expect(header['sourceVersion'], '2026-09-19');
|
|
expect(
|
|
[
|
|
for (final credit in header['credits']! as List)
|
|
(credit as Map)['name'],
|
|
],
|
|
['한국어기초사전 (Basic Korean Dictionary)'],
|
|
);
|
|
final school = entries.singleWhere((entry) => entry['headword'] == '학교');
|
|
expect(school['id'], '73276');
|
|
expect(school['reading'], '학꾜');
|
|
expect(school['labels'], ['level:beginner']);
|
|
expect(school['keys'], containsAll(['학교', '學校', 'school']));
|
|
const definition =
|
|
'An institution that educates students according to a specific '
|
|
'purpose, curriculum, system, etc.';
|
|
expect(school['entry'], {
|
|
'pos': 'noun',
|
|
'hanja': ['學校'],
|
|
'senses': [
|
|
{
|
|
'glosses': ['school'],
|
|
'info': [definition],
|
|
},
|
|
],
|
|
});
|
|
final read = entries.singleWhere((entry) => entry['headword'] == '읽다');
|
|
expect(read['reading'], '익따');
|
|
expect(read['textKeys'], containsAll(['읽다', '읽어', '읽고']));
|
|
expect(
|
|
(read['entry']! as Map)['inflections'],
|
|
contains(equals({'text': '읽어'})),
|
|
);
|
|
final together = entries.where((entry) => entry['headword'] == '같이');
|
|
expect(
|
|
[for (final entry in together) (entry['entry']! as Map)['pos']],
|
|
['particle', 'adv'],
|
|
);
|
|
});
|
|
|
|
test('an idiom or a proverb is an entry of its own, never found in running '
|
|
'text, with an id told apart from its word', () async {
|
|
final [_, ...entries] = await converted();
|
|
|
|
final proverb = entries.singleWhere(
|
|
(entry) => entry['headword'] == '가는 날이 장날',
|
|
);
|
|
expect(proverb['id'], '27500:가는 날이 장날');
|
|
expect((proverb['entry']! as Map)['pos'], 'proverb');
|
|
expect(proverb.containsKey('textKeys'), isFalse);
|
|
expect(
|
|
entries.singleWhere((entry) => entry['headword'] == '가다')['id'],
|
|
'27500',
|
|
);
|
|
});
|
|
|
|
test("Wiktionary's forms find a word in text; a verb rarely written as "
|
|
'the dictionary writes it ranks by its most used form', () async {
|
|
final korean = WiktionaryKorean()..forms['가다'] = {'가요', '갔어요', '갑니다'};
|
|
final [_, ...entries] = await converted(
|
|
korean: korean,
|
|
frequencies: WordFrequencies(const {'갔어요': 50}),
|
|
);
|
|
|
|
final go = entries.singleWhere((entry) => entry['headword'] == '가다');
|
|
expect(go['textKeys'], containsAll(['가다', '가요', '갔어요']));
|
|
expect(go['labels'], contains('common'));
|
|
expect(go['rank'], 100000 + 100);
|
|
});
|
|
|
|
test("the export's JSON download is the third its page offers", () {
|
|
expect(
|
|
krdictJsonDownloadOf('''
|
|
<button onclick="batchDownload('215')">엑셀</button>
|
|
<button onclick="batchDownload('216')">XML</button>
|
|
<button onclick="batchDownload('217')">Json</button>'''),
|
|
'217',
|
|
);
|
|
expect(krdictJsonDownloadOf("batchDownload('0')"), isNull);
|
|
});
|
|
}
|