Halkyon's shared packages
A pub workspace of the Dart packages Halkyon's apps share: halkyon_ui, Halkyon's look (the theme, fonts, layout and shared widgets, with texts in English and German), halkyon_core's low-level primitives and halkyon_interop's cross-app contracts. Dart 3.13 with LeanCode's lints, configured once at the root; apps take a package by git, pinned to a signed commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
commit
80fa7b9628
1 changed files with 6762 additions and 0 deletions
174
packages/halkyon_interop/test/halkyon_interop_test.dart
Normal file
174
packages/halkyon_interop/test/halkyon_interop_test.dart
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import 'package:halkyon_interop/halkyon_interop.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('TaskShape', () {
|
||||
final fixedTime = DateTime.utc(2026, 1, 15, 12);
|
||||
final deepLink = Uri.parse('halkyon-learn://review/spanish-vocab');
|
||||
|
||||
TaskShape build({
|
||||
String sourceAppId = 'eu.halkyon.learn',
|
||||
String externalId = 'review-12',
|
||||
String title = 'Review 12 cards',
|
||||
String? subtitle = 'Spanish vocabulary',
|
||||
DateTime? dueAt,
|
||||
Uri? linkOverride,
|
||||
}) => TaskShape(
|
||||
sourceAppId: sourceAppId,
|
||||
externalId: externalId,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
dueAt: dueAt ?? fixedTime,
|
||||
deepLink: linkOverride ?? deepLink,
|
||||
);
|
||||
|
||||
test('exposes provided fields', () {
|
||||
final task = build();
|
||||
expect(task.sourceAppId, 'eu.halkyon.learn');
|
||||
expect(task.externalId, 'review-12');
|
||||
expect(task.title, 'Review 12 cards');
|
||||
expect(task.subtitle, 'Spanish vocabulary');
|
||||
expect(task.dueAt, fixedTime);
|
||||
expect(task.deepLink, deepLink);
|
||||
});
|
||||
|
||||
test('allows null for optional fields', () {
|
||||
final task = TaskShape(
|
||||
sourceAppId: 'eu.halkyon.learn',
|
||||
externalId: 'review-12',
|
||||
title: 'Review',
|
||||
deepLink: deepLink,
|
||||
);
|
||||
expect(task.subtitle, isNull);
|
||||
expect(task.dueAt, isNull);
|
||||
});
|
||||
|
||||
test('two instances with identical fields are equal', () {
|
||||
expect(build(), equals(build()));
|
||||
expect(build().hashCode, equals(build().hashCode));
|
||||
});
|
||||
|
||||
test('differs on any field change', () {
|
||||
expect(build(), isNot(equals(build(title: 'Different'))));
|
||||
expect(build(), isNot(equals(build(externalId: 'different-id'))));
|
||||
expect(build(), isNot(equals(build(sourceAppId: 'eu.halkyon.todo'))));
|
||||
expect(build(), isNot(equals(build(dueAt: DateTime.utc(2026, 2)))));
|
||||
expect(
|
||||
build(),
|
||||
isNot(equals(build(linkOverride: Uri.parse('halkyon-todo://x')))),
|
||||
);
|
||||
});
|
||||
|
||||
test('toString includes identifying fields', () {
|
||||
final str = build().toString();
|
||||
expect(str, contains('eu.halkyon.learn'));
|
||||
expect(str, contains('review-12'));
|
||||
expect(str, contains('Review 12 cards'));
|
||||
});
|
||||
});
|
||||
|
||||
group('HalkyonEvent / ReviewDueEvent', () {
|
||||
final fixedTime = DateTime.utc(2026, 1, 15, 12);
|
||||
|
||||
test('ReviewDueEvent is a HalkyonEvent', () {
|
||||
final event = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
expect(event, isA<HalkyonEvent>());
|
||||
});
|
||||
|
||||
test('exposes provided fields', () {
|
||||
final event = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
expect(event.occurredAt, fixedTime);
|
||||
expect(event.studysetId, 'spanish-vocab');
|
||||
expect(event.dueCount, 12);
|
||||
});
|
||||
|
||||
test('events with identical fields are equal', () {
|
||||
final a = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
final b = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
expect(a, equals(b));
|
||||
expect(a.hashCode, equals(b.hashCode));
|
||||
});
|
||||
|
||||
test('differs on field changes', () {
|
||||
final base = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
expect(
|
||||
base,
|
||||
isNot(
|
||||
equals(
|
||||
ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 13,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
base,
|
||||
isNot(
|
||||
equals(
|
||||
ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'french-vocab',
|
||||
dueCount: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('switch on sealed type is exhaustive', () {
|
||||
final HalkyonEvent event = ReviewDueEvent(
|
||||
occurredAt: fixedTime,
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 12,
|
||||
);
|
||||
|
||||
// If a new HalkyonEvent subtype is added without updating this switch,
|
||||
// the analyzer/compiler will flag it. That's the value of `sealed`.
|
||||
final description = switch (event) {
|
||||
ReviewDueEvent(:final studysetId, :final dueCount) =>
|
||||
'$dueCount cards due in $studysetId',
|
||||
};
|
||||
|
||||
expect(description, '12 cards due in spanish-vocab');
|
||||
});
|
||||
});
|
||||
|
||||
group('DormantPublisher', () {
|
||||
test('accepts publish calls without throwing', () async {
|
||||
const publisher = DormantPublisher();
|
||||
final event = ReviewDueEvent(
|
||||
occurredAt: DateTime.utc(2026, 1, 15),
|
||||
studysetId: 'spanish-vocab',
|
||||
dueCount: 5,
|
||||
);
|
||||
await expectLater(publisher.publish(event), completes);
|
||||
});
|
||||
|
||||
test('implements EventPublisher', () {
|
||||
const publisher = DormantPublisher();
|
||||
expect(publisher, isA<EventPublisher>());
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in a new issue