Skip to content

Commit 0e17eb9

Browse files
committed
feat: add tests
1 parent 5c4411a commit 0e17eb9

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed

catalyst_voices/packages/internal/catalyst_voices_repositories/test/src/database/dao/proposals_v2_dao_test.dart

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart';
66
import 'package:catalyst_voices_repositories/src/database/catalyst_database.dart';
77
import 'package:catalyst_voices_repositories/src/database/dao/proposals_v2_dao.dart';
88
import 'package:catalyst_voices_repositories/src/database/model/document_composite_entity.dart';
9+
import 'package:catalyst_voices_repositories/src/database/model/raw_proposal_entity.dart';
910
import 'package:catalyst_voices_repositories/src/database/table/documents_local_metadata.drift.dart';
1011
import 'package:catalyst_voices_repositories/src/dto/proposal/proposal_submission_action_dto.dart';
1112
import 'package:collection/collection.dart';
@@ -5886,6 +5887,346 @@ void main() {
58865887
await subscription.cancel();
58875888
});
58885889
});
5890+
5891+
group('watchProposal', () {
5892+
final now = DateTime.now();
5893+
final earlier = now.subtract(const Duration(hours: 1));
5894+
final latest = now.add(const Duration(hours: 1));
5895+
5896+
test('returns null when proposal does not exist', () async {
5897+
const ref = SignedDocumentRef(id: 'non-existent', ver: 'v1');
5898+
5899+
await expectLater(
5900+
dao.watchProposal(id: ref),
5901+
emits(isNull),
5902+
);
5903+
});
5904+
5905+
test('returns proposal with draft status when no action exists', () async {
5906+
final author = _createTestAuthor();
5907+
final proposalVer = _buildUuidV7At(now);
5908+
final proposal = _createTestDocumentEntity(
5909+
id: 'p1',
5910+
ver: proposalVer,
5911+
authors: [author],
5912+
);
5913+
5914+
await db.documentsV2Dao.saveAll([proposal]);
5915+
5916+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
5917+
5918+
await expectLater(
5919+
dao.watchProposal(id: ref),
5920+
emits(
5921+
isA<RawProposalEntity?>()
5922+
.having((e) => e?.proposal.id, 'proposal.id', proposal.doc.id)
5923+
.having((e) => e?.proposal.ver, 'proposal.ver', proposal.doc.ver)
5924+
.having((e) => e?.actionType, 'actionType', ProposalSubmissionAction.draft),
5925+
),
5926+
);
5927+
});
5928+
5929+
test('returns proposal with final status when final action exists', () async {
5930+
final author = _createTestAuthor();
5931+
final proposalVer = _buildUuidV7At(now);
5932+
final proposal = _createTestDocumentEntity(
5933+
id: proposalVer,
5934+
ver: proposalVer,
5935+
authors: [author],
5936+
);
5937+
5938+
final finalAction = _createTestDocumentEntity(
5939+
id: 'action-1',
5940+
ver: _buildUuidV7At(latest),
5941+
type: DocumentType.proposalActionDocument,
5942+
refId: proposal.doc.id,
5943+
refVer: proposal.doc.ver,
5944+
contentData: ProposalSubmissionActionDto.aFinal.toJson(),
5945+
authors: [author],
5946+
);
5947+
5948+
await db.documentsV2Dao.saveAll([proposal, finalAction]);
5949+
5950+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
5951+
5952+
await expectLater(
5953+
dao.watchProposal(id: ref),
5954+
emits(
5955+
isA<RawProposalEntity?>().having(
5956+
(e) => e?.actionType,
5957+
'actionType',
5958+
ProposalSubmissionAction.aFinal,
5959+
),
5960+
),
5961+
);
5962+
});
5963+
5964+
test('returns proposal with hide status when latest action is hide', () async {
5965+
final author = _createTestAuthor();
5966+
final proposalVer = _buildUuidV7At(now);
5967+
final proposal = _createTestDocumentEntity(
5968+
id: proposalVer,
5969+
ver: proposalVer,
5970+
authors: [author],
5971+
);
5972+
5973+
final hideAction = _createTestDocumentEntity(
5974+
id: 'action-1',
5975+
ver: _buildUuidV7At(latest),
5976+
type: DocumentType.proposalActionDocument,
5977+
refId: proposal.doc.id,
5978+
refVer: proposal.doc.ver,
5979+
contentData: ProposalSubmissionActionDto.hide.toJson(),
5980+
authors: [author],
5981+
);
5982+
5983+
await db.documentsV2Dao.saveAll([proposal, hideAction]);
5984+
5985+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
5986+
5987+
await expectLater(
5988+
dao.watchProposal(id: ref),
5989+
emits(
5990+
isA<RawProposalEntity?>().having(
5991+
(e) => e?.actionType,
5992+
'actionType',
5993+
ProposalSubmissionAction.hide,
5994+
),
5995+
),
5996+
);
5997+
});
5998+
5999+
test('returns hide when latest action (any version) is hide', () async {
6000+
final author = _createTestAuthor();
6001+
final proposalV1Ver = _buildUuidV7At(earlier);
6002+
final proposalV2Ver = _buildUuidV7At(now);
6003+
6004+
// First version must have id == ver for valid_actions CTE
6005+
final proposalV1 = _createTestDocumentEntity(
6006+
id: proposalV1Ver,
6007+
ver: proposalV1Ver,
6008+
authors: [author],
6009+
);
6010+
6011+
final proposalV2 = _createTestDocumentEntity(
6012+
id: proposalV1Ver,
6013+
ver: proposalV2Ver,
6014+
authors: [author],
6015+
);
6016+
6017+
// Final action on V1
6018+
final finalActionV1 = _createTestDocumentEntity(
6019+
id: 'action-1',
6020+
ver: _buildUuidV7At(now),
6021+
type: DocumentType.proposalActionDocument,
6022+
refId: proposalV1.doc.id,
6023+
refVer: proposalV1.doc.ver,
6024+
contentData: ProposalSubmissionActionDto.aFinal.toJson(),
6025+
authors: [author],
6026+
);
6027+
6028+
// Hide action on V2 (latest action overall)
6029+
final hideActionV2 = _createTestDocumentEntity(
6030+
id: 'action-2',
6031+
ver: _buildUuidV7At(latest),
6032+
type: DocumentType.proposalActionDocument,
6033+
refId: proposalV2.doc.id,
6034+
refVer: proposalV2.doc.ver,
6035+
contentData: ProposalSubmissionActionDto.hide.toJson(),
6036+
authors: [author],
6037+
);
6038+
6039+
await db.documentsV2Dao.saveAll([
6040+
proposalV1,
6041+
proposalV2,
6042+
finalActionV1,
6043+
hideActionV2,
6044+
]);
6045+
6046+
// Query V1 - should return hide because latest action (any ver) is hide
6047+
final ref = SignedDocumentRef(id: proposalV1.doc.id, ver: proposalV1.doc.ver);
6048+
6049+
await expectLater(
6050+
dao.watchProposal(id: ref),
6051+
emits(
6052+
isA<RawProposalEntity?>().having(
6053+
(e) => e?.actionType,
6054+
'actionType',
6055+
ProposalSubmissionAction.hide,
6056+
),
6057+
),
6058+
);
6059+
});
6060+
6061+
test('includes template when available', () async {
6062+
final author = _createTestAuthor();
6063+
const templateRef = SignedDocumentRef(id: 'template-1', ver: 'template-v1');
6064+
6065+
final template = _createTestDocumentEntity(
6066+
id: templateRef.id,
6067+
ver: templateRef.ver,
6068+
type: DocumentType.proposalTemplate,
6069+
authors: [author],
6070+
);
6071+
6072+
final proposalVer = _buildUuidV7At(now);
6073+
final proposal = _createTestDocumentEntity(
6074+
id: 'p1',
6075+
ver: proposalVer,
6076+
templateId: templateRef.id,
6077+
templateVer: templateRef.ver,
6078+
authors: [author],
6079+
);
6080+
6081+
await db.documentsV2Dao.saveAll([template, proposal]);
6082+
6083+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
6084+
6085+
await expectLater(
6086+
dao.watchProposal(id: ref),
6087+
emits(
6088+
isA<RawProposalEntity?>()
6089+
.having((e) => e?.template, 'template', isNotNull)
6090+
.having((e) => e?.template?.id, 'template.id', templateRef.id),
6091+
),
6092+
);
6093+
});
6094+
6095+
test('includes favorite status', () async {
6096+
final author = _createTestAuthor();
6097+
final proposalVer = _buildUuidV7At(now);
6098+
final proposal = _createTestDocumentEntity(
6099+
id: 'p1',
6100+
ver: proposalVer,
6101+
authors: [author],
6102+
);
6103+
6104+
await db.documentsV2Dao.saveAll([proposal]);
6105+
6106+
// Mark as favorite
6107+
await dao.updateProposalFavorite(id: proposal.doc.id, isFavorite: true);
6108+
6109+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
6110+
6111+
await expectLater(
6112+
dao.watchProposal(id: ref),
6113+
emits(
6114+
isA<RawProposalEntity?>().having((e) => e?.isFavorite, 'isFavorite', isTrue),
6115+
),
6116+
);
6117+
});
6118+
6119+
test('includes version IDs', () async {
6120+
final author = _createTestAuthor();
6121+
final proposalV1Ver = _buildUuidV7At(earlier);
6122+
final proposalV2Ver = _buildUuidV7At(now);
6123+
6124+
final proposalV1 = _createTestDocumentEntity(
6125+
id: 'p1',
6126+
ver: proposalV1Ver,
6127+
authors: [author],
6128+
);
6129+
6130+
final proposalV2 = _createTestDocumentEntity(
6131+
id: 'p1',
6132+
ver: proposalV2Ver,
6133+
authors: [author],
6134+
);
6135+
6136+
await db.documentsV2Dao.saveAll([proposalV1, proposalV2]);
6137+
6138+
final ref = SignedDocumentRef(id: proposalV1.doc.id, ver: proposalV1.doc.ver);
6139+
6140+
await expectLater(
6141+
dao.watchProposal(id: ref),
6142+
emits(
6143+
isA<RawProposalEntity?>()
6144+
.having((e) => e?.versionIds, 'versionIds', hasLength(2))
6145+
.having((e) => e?.versionIds, 'versionIds', contains(proposalV1Ver))
6146+
.having((e) => e?.versionIds, 'versionIds', contains(proposalV2Ver)),
6147+
),
6148+
);
6149+
});
6150+
6151+
test('includes comments count', () async {
6152+
final author = _createTestAuthor();
6153+
final proposalVer = _buildUuidV7At(now);
6154+
final proposal = _createTestDocumentEntity(
6155+
id: 'p1',
6156+
ver: proposalVer,
6157+
authors: [author],
6158+
);
6159+
6160+
final comment1 = _createTestDocumentEntity(
6161+
id: 'c1',
6162+
ver: _buildUuidV7At(now),
6163+
type: DocumentType.commentDocument,
6164+
refId: proposal.doc.id,
6165+
refVer: proposal.doc.ver,
6166+
authors: [author],
6167+
);
6168+
6169+
final comment2 = _createTestDocumentEntity(
6170+
id: 'c2',
6171+
ver: _buildUuidV7At(latest),
6172+
type: DocumentType.commentDocument,
6173+
refId: proposal.doc.id,
6174+
refVer: proposal.doc.ver,
6175+
authors: [author],
6176+
);
6177+
6178+
await db.documentsV2Dao.saveAll([proposal, comment1, comment2]);
6179+
6180+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
6181+
6182+
await expectLater(
6183+
dao.watchProposal(id: ref),
6184+
emits(
6185+
isA<RawProposalEntity?>().having((e) => e?.commentsCount, 'commentsCount', 2),
6186+
),
6187+
);
6188+
});
6189+
6190+
test('stream emits updated value when data changes', () async {
6191+
final author = _createTestAuthor();
6192+
final proposalVer = _buildUuidV7At(now);
6193+
final proposal = _createTestDocumentEntity(
6194+
id: proposalVer,
6195+
ver: proposalVer,
6196+
authors: [author],
6197+
);
6198+
6199+
await db.documentsV2Dao.saveAll([proposal]);
6200+
6201+
final ref = SignedDocumentRef(id: proposal.doc.id, ver: proposal.doc.ver);
6202+
final emissions = <RawProposalEntity?>[];
6203+
6204+
final subscription = dao.watchProposal(id: ref).listen(emissions.add);
6205+
6206+
await pumpEventQueue();
6207+
expect(emissions, hasLength(1));
6208+
expect(emissions[0]?.actionType, ProposalSubmissionAction.draft);
6209+
6210+
// Add final action
6211+
final finalAction = _createTestDocumentEntity(
6212+
id: 'action-1',
6213+
ver: _buildUuidV7At(latest),
6214+
type: DocumentType.proposalActionDocument,
6215+
refId: proposal.doc.id,
6216+
refVer: proposal.doc.ver,
6217+
contentData: ProposalSubmissionActionDto.aFinal.toJson(),
6218+
authors: [author],
6219+
);
6220+
6221+
await db.documentsV2Dao.saveAll([finalAction]);
6222+
await pumpEventQueue();
6223+
6224+
expect(emissions, hasLength(2));
6225+
expect(emissions[1]?.actionType, ProposalSubmissionAction.aFinal);
6226+
6227+
await subscription.cancel();
6228+
});
6229+
});
58896230
},
58906231
skip: driftSkip,
58916232
);

0 commit comments

Comments
 (0)