fix(publish): a release sends exactly the tag names the sheet showed (ADR 0019)

The final reading of 22 Oct found that the names were worked out again
after the sync that comes before publishing, so a link to a private tag
the instance merged in during that sync sent a name nobody saw going. The
sheet now hands what it showed to Publish, and the device refuses before
anything goes out when the names worked out then differ
(PublishProblem.tagNamesChanged), reading them again for the person to
look at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-22 11:30:17 +02:00
commit 92ff7616a4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
3 changed files with 164 additions and 13 deletions

View file

@ -1,11 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_learn_flutter/src/feature/publish/data/publish_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/publish/model/publication.dart';
import '../../../support/fake_publish_gateway.dart';
import '../../../support/memory_published_store.dart';
/// The instance can't read the person's tags (ADR 0019, D2): publishing
/// names them as this device reads them.
/// names them as this device reads them, exactly as the sheet showed them.
void main() {
test('publishing sends the names of the tags the release carries, as this '
'device reads them, and no others', () async {
@ -21,7 +22,12 @@ void main() {
);
addTearDown(publishing.dispose);
await publishing.publish('studyset-1', slug: 'japanese', notes: '');
await publishing.publish(
'studyset-1',
slug: 'japanese',
notes: '',
tagNamesShown: {'tag-1': 'JLPT-N5'},
);
expect(gateway.lastTagNames, {'tag-1': 'JLPT-N5'});
expect(asked, ['studyset-1']);
@ -32,8 +38,67 @@ void main() {
final publishing = PublishRepository(gateway, MemoryPublishedStore());
addTearDown(publishing.dispose);
await publishing.publish('studyset-1', slug: 'japanese', notes: '');
await publishing.publish(
'studyset-1',
slug: 'japanese',
notes: '',
tagNamesShown: null,
);
expect(gateway.lastTagNames, isNull);
});
test('names that changed since the sheet showed them (a sync merged a tag '
'in) are never sent: publishing is refused here', () async {
final gateway = FakePublishGateway();
var names = {'tag-1': 'JLPT-N5'};
final publishing = PublishRepository(
gateway,
MemoryPublishedStore(),
tagNames: (studysetId) async => names,
);
addTearDown(publishing.dispose);
final shown = await publishing.tagNamesOf('studyset-1');
// The sync before publishing brings a link to a private tag.
names = {'tag-1': 'JLPT-N5', 'tag-2': 'あかね噺 S1 E1'};
await expectLater(
publishing.publish(
'studyset-1',
slug: 'japanese',
notes: '',
tagNamesShown: shown,
),
throwsA(
isA<PublishRefused>().having(
(e) => e.problem,
'problem',
PublishProblem.tagNamesChanged,
),
),
);
expect(gateway.lastTagNames, isNull, reason: 'nothing went out');
});
test('names never shown are never sent', () async {
final gateway = FakePublishGateway();
final publishing = PublishRepository(
gateway,
MemoryPublishedStore(),
tagNames: (studysetId) async => {'tag-1': 'JLPT-N5'},
);
addTearDown(publishing.dispose);
await expectLater(
publishing.publish(
'studyset-1',
slug: 'japanese',
notes: '',
tagNamesShown: null,
),
throwsA(isA<PublishRefused>()),
);
expect(gateway.lastTagNames, isNull);
});
}

View file

@ -63,6 +63,7 @@ void main() {
slug: 'japanese',
notes: 'First lessons.',
subtree: ['japanese'],
tagNames: null,
),
);
return await bloc.stream.firstWhere(
@ -165,7 +166,12 @@ void main() {
publishing.fail = Exception('offline');
publisher.add(
const PublishEventRequested(slug: 'japanese', notes: '', subtree: []),
const PublishEventRequested(
slug: 'japanese',
notes: '',
subtree: [],
tagNames: null,
),
);
final failed = await publisher.stream.firstWhere(
(state) => state is PublishStateReady && !state.publishing,

View file

@ -46,7 +46,12 @@ void main() {
await pumpEventQueue();
await publishing.publication('japanese');
await publishing.publish('kanji', slug: 'kanji', notes: '');
await publishing.publish(
'kanji',
slug: 'kanji',
notes: '',
tagNamesShown: null,
);
await publishing.withdraw('japanese');
await pumpEventQueue();