feat(learn): sharing moves a studyset into a space of its own
Step 4 (d) of ADR 0044 (D14, D23, D27, D28), without its screens yet. - `StudysetSharing`: refuses a studyset with a publication in its tree and a device that can't seal, before anything is made; makes the space, readies it, exports the tree with its progress, imports it under new IDs with its top placed in the space, and deletes the old one for good once the copy is whole. Failing or aborted, the space is deleted and the studyset stays as it was. - `SyncSpaceSetup`: syncs until the person's membership of the new space came, then makes its first key and seals it to them. - `Privacy` writes a top studyset placed in a space with that space's key; a studyset can be inserted under an ID chosen beforehand. - The archive's export and import take `moved`, keeping each item's link to Explore and each card's origin. - An import that fails midway takes back what it wrote, as an aborted one did. Whats-New: An import that fails partway no longer leaves part of the studyset behind. Whats-New-De: Ein Import, der unterwegs scheitert, lässt kein halbes Lernset mehr zurück. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
f0b05a5e9a
commit
d3e8b7d1c6
1 changed files with 1001 additions and 41 deletions
|
|
@ -264,6 +264,7 @@ void main() {
|
|||
late PdfSourceRepository pdfs;
|
||||
late AssetRepository assets;
|
||||
late FakeAssetGateway assetRows;
|
||||
late FakeReviewGateway reviewRows;
|
||||
late ArchiveImporter importer;
|
||||
|
||||
setUp(() {
|
||||
|
|
@ -285,7 +286,8 @@ void main() {
|
|||
FakeAssetSealing(),
|
||||
);
|
||||
pdfs = PdfSourceRepository(FakePdfSourceGateway(), assets);
|
||||
reviews = ReviewRepository(FakeReviewGateway());
|
||||
reviewRows = FakeReviewGateway();
|
||||
reviews = ReviewRepository(reviewRows);
|
||||
importer = ArchiveImporter(
|
||||
ReleaseCopier(studysets, cards, notes, paths, pdfs, tags, assets),
|
||||
DictionaryRepository(
|
||||
|
|
@ -413,13 +415,35 @@ void main() {
|
|||
throwsA(isA<WorkAborted>()),
|
||||
);
|
||||
|
||||
// The reviews written go with their cards: the database
|
||||
// cascades (`review_log.card`), which the fakes don't.
|
||||
// The reviews written go with their cards: the card gateway
|
||||
// deletes them with the card (ADR 0044, D26), which the fakes
|
||||
// don't.
|
||||
expect(await studysets.withDeleted(), isEmpty);
|
||||
expect(await cards.allCards(), isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
test('failing midway, deletes everything it wrote, as aborted '
|
||||
'(11 Nov 2026)', () async {
|
||||
final own = await studysets.create(StudysetContent.titled('Own'));
|
||||
final ownFile = await assets.add(
|
||||
kept,
|
||||
fileName: 'mine.png',
|
||||
mimeType: 'image/png',
|
||||
studysetId: own,
|
||||
);
|
||||
reviewRows.failWrites = Exception('the disk is full');
|
||||
|
||||
await expectLater(
|
||||
importer.import(readArchive(zip(withFiles()))),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
|
||||
expect([for (final s in await studysets.withDeleted()) s.id], [own]);
|
||||
expect(await cards.allCards(), isEmpty);
|
||||
expect([for (final row in await assetRows.all()) row.id], [ownFile]);
|
||||
});
|
||||
|
||||
test('asked once all of it is written, it is done', () async {
|
||||
final abort = AbortSignal();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue