Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1350,7 +1350,7 @@ packages:
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
dependency: "direct dev"
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
Expand Down Expand Up @@ -1382,7 +1382,7 @@ packages:
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
dependency: "direct dev"
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
Expand Down
5 changes: 5 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ dev_dependencies:
# Mocktail: null-safety-native mock library, no codegen, no
# mockito @GenerateMocks boilerplate. Companion to bloc_test.
mocktail: ^1.0.4
# Direct deps for test/_helpers/fake_path_provider.dart (platform-interface
# fakes). Both already resolve transitively via path_provider; declaring
# them keeps depend_on_referenced_packages honest.
path_provider_platform_interface: ^2.1.0
plugin_platform_interface: ^2.1.0

flutter_lints: ^2.0.0
build_runner: ^2.13.0
Expand Down
11 changes: 9 additions & 2 deletions test/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,26 @@ below, or add one if the shape is missing.
| Helper file | Exposes | Use for |
|---|---|---|
| `isar_test_harness.dart` | `openTestIsar()`, `groupSeed`, `privateGroupSeed`, `followedUserSeed`, `followedNoteSeed` | Opening an isolated on-disk Isar in `setUp` + one-liner seed rows for those specific collections. |
| `isar_seeds.dart` | `seedNoteRow`, `seedUnreadRow`, `seedRelationEdge`, `seedReport` | Direct writes into `Note`, `UnreadNote`, `NoteRelation`, `Report` collections. Each helper wraps its own `writeTxn`, safe to call ad-hoc from any test using `openTestIsar()`. |
| `isar_seeds.dart` | builders `noteRow`, `unreadRow`, `relationEdge`, `reportRow`, `deletedNoteRow`, `eventQueueRow`, `profileRow`, `dmConversationRow`, `relayRow`, `savedNoteRow`, `shivConversationRow`, `shivMessageRow`, `mediaAttachmentRow` + committers `seedNoteRow`, `seedUnreadRow`, `seedRelationEdge`, `seedReport`, `seedDeletedNote`, `seedProfile`, `seedDmConversation`, `seedRelay` | Isar model rows for `Note`, `UnreadNote`, `NoteRelation`, `Report`, `DeletedNote`, `EventQueue`, `NostrProfile`, `DmConversation`, `Relay`, `SavedNote`, `ShivConversation`, `ShivMessage`. Builders return the model (batch them in one `writeTxn`); committers wrap their own `writeTxn`. |
| `recording_event_queue.dart` | `RecordingEventQueue`, `EnqueueCall` | Any repo that publishes through `EventQueueRepository.enqueueSignedEvent`. Configure `leftOnEnqueue` / `throwOnEnqueue` to simulate failure. Inspect `.calls` to assert wire shape. |
| `fake_note_relations.dart` | `FakeNoteRelations` | Any repo that reads from `NoteRelationRepository`. Seed `.children[parentId]` / `.parents[childId]` before the test runs. |
| `stub_user_repository.dart` | `StubUserRepository` | Any repo that calls `UserRepository.getActiveKeysHex()`. Set `.keys = null` to simulate a logged-out identity. |
| `stub_user_repository.dart` | `StubUserRepository` | Any repo that calls `UserRepository.getActiveKeysHex()` or `getActiveUser()` (both derive from `.keys`). Set `.keys = null` to simulate a logged-out identity. |
| `stub_followed_users.dart` | `StubFollowedUsers` | Any repo that reads the follow list via `FollowedUserRepository.getAllPubkeys()`. Seed `.pubkeys`; set `.leftOnGetAllPubkeys` to simulate failure. |
| `fake_path_provider.dart` | `FakePathProviderPlatform` | Code that calls `getApplicationDocumentsDirectory()` / `getApplicationSupportDirectory()`. Install via `PathProviderPlatform.instance = FakePathProviderPlatform(docs: ..., support: ...)` pointing at temp dirs. |

### Constants in `fixtures.dart`

```dart
kTestPrivHex, kTestPubHex, kSigningKeys, aSigningKeys(...) // signing keys
kSampleEventIdHex // 64-char hex event id (for NIP-56 / nostr code paths)
kSampleTargetPubkeyHex // 64-char hex pubkey (same, for target-user args)
tOwnProfileSentinel // DateTime(3000,6,1) own-profile eviction sentinel
```

Isar round-trips `DateTime` as **local time** and SharedPreferences-backed
stores truncate to millis — compare instants (`isAtSameMomentAs`) or epoch
millis, never `==` against a UTC fixture.

Use `kSampleEventIdHex` / `kSampleTargetPubkeyHex` when the code under
test validates hex shape (schnorr sig, NIP-56 tag verify, etc). Use the
short `kAlicePub` / `kBobPub` when the value is just an opaque identifier.
Expand Down
24 changes: 24 additions & 0 deletions test/_helpers/fake_path_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

/// Deterministic [PathProviderPlatform] test double.
///
/// Points `getApplicationDocumentsDirectory()` / `getApplicationSupportDirectory()`
/// at caller-chosen temp dirs. Install in `setUp` via
/// `PathProviderPlatform.instance = FakePathProviderPlatform(docs: ..., support: ...)`.
class FakePathProviderPlatform extends PathProviderPlatform
with MockPlatformInterfaceMixin {
FakePathProviderPlatform({required this.docs, required this.support});

final String docs;
final String support;

@override
Future<String?> getApplicationDocumentsPath() async => docs;

@override
Future<String?> getApplicationSupportPath() async => support;

@override
Future<String?> getTemporaryPath() async => support;
}
98 changes: 98 additions & 0 deletions test/_helpers/fixtures.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
// - Times default to [tT0] / [tNow] so tests are deterministic.

import 'package:uniun/core/enum/note_type.dart';
import 'package:uniun/core/enum/relay_status.dart';
import 'package:uniun/core/notes/note_kinds.dart';
import 'package:uniun/domain/entities/dm/dm_conversation_entity.dart';
import 'package:uniun/domain/entities/graph_edge/graph_edge_entity.dart';
import 'package:uniun/domain/entities/graph_node/graph_node_entity.dart';
import 'package:uniun/domain/entities/manas/manas_entity.dart';
import 'package:uniun/domain/entities/media/media_blob_entity.dart';
import 'package:uniun/domain/entities/media/media_dim.dart';
import 'package:uniun/domain/entities/memory_node/memory_node_entity.dart';
import 'package:uniun/domain/entities/note/note_entity.dart';
import 'package:uniun/domain/entities/profile/profile_entity.dart';
import 'package:uniun/domain/entities/relay/relay_entity.dart';
import 'package:uniun/domain/entities/saved_note/saved_note_entity.dart';
import 'package:uniun/domain/entities/user_key/user_key_entity.dart';
import 'package:uniun/domain/usecases/user_usecases.dart';
Expand All @@ -24,6 +30,10 @@ import 'package:uniun/domain/usecases/user_usecases.dart';
final DateTime tT0 = DateTime.utc(2026, 1, 1);
final DateTime tNow = DateTime.utc(2026, 6, 30, 12, 0, 0);

/// Own-profile eviction sentinel — mirrors the production value the
/// CleanupManager never reaches (`ProfileModel.lastSeenAt` docs).
final DateTime tOwnProfileSentinel = DateTime(3000, 6, 1);

// ── Predefined pubkeys ───────────────────────────────────────────────────────

const String kSelfPub = 'self-pub';
Expand Down Expand Up @@ -286,6 +296,40 @@ ProfileEntity aProfile({
ProfileEntity anAnonymousProfile({String pubkey = kAlicePub}) =>
aProfile(pubkey: pubkey, name: null, username: null);

// ── DmConversationEntity ─────────────────────────────────────────────────────

DmConversationEntity aDmConversation({
int id = 0,
String otherPubkey = kSampleTargetPubkeyHex,
List<String> relays = const [],
}) {
return DmConversationEntity(
id: id,
otherPubkey: otherPubkey,
relays: relays,
);
}

// ── RelayEntity ──────────────────────────────────────────────────────────────

RelayEntity aRelay({
String url = 'wss://relay.example',
bool read = true,
bool write = true,
RelayStatus status = RelayStatus.disconnected,
DateTime? lastConnectedAt,
bool isSystem = false,
}) {
return RelayEntity(
url: url,
read: read,
write: write,
status: status,
lastConnectedAt: lastConnectedAt,
isSystem: isSystem,
);
}

// ── SavedNoteEntity ──────────────────────────────────────────────────────────

SavedNoteEntity aSavedNote({
Expand Down Expand Up @@ -354,6 +398,60 @@ List<ManasEntity> manyManas(int n) => [
for (var i = 0; i < n; i++) aManas(manasId: 'm-$i', name: 'Manas $i'),
];

// ── Graph entities ───────────────────────────────────────────────────────────

GraphNodeEntity aGraphNode({
String key = 'node-1',
String name = 'Node One',
String type = 'topic',
DateTime? createdAt,
DateTime? updatedAt,
}) {
return GraphNodeEntity(
key: key,
name: name,
type: type,
createdAt: createdAt ?? tT0,
updatedAt: updatedAt ?? tNow,
);
}

GraphEdgeEntity aGraphEdge({
String sourceKey = 'node-1',
String targetKey = 'node-2',
String relationType = 'mentions',
String sourceNoteId = 'note-1',
DateTime? createdAt,
}) {
return GraphEdgeEntity(
sourceKey: sourceKey,
targetKey: targetKey,
relationType: relationType,
sourceNoteId: sourceNoteId,
createdAt: createdAt ?? tNow,
);
}

// ── MemoryNodeEntity ─────────────────────────────────────────────────────────

MemoryNodeEntity aMemoryNode({
String noteId = 'note-1',
String summary = 'a wiki-style summary',
List<String> keyPoints = const ['point'],
List<String> concepts = const ['concept'],
List<String> linkedNoteIds = const [],
DateTime? updatedAt,
}) {
return MemoryNodeEntity(
noteId: noteId,
summary: summary,
keyPoints: keyPoints,
concepts: concepts,
linkedNoteIds: linkedNoteIds,
updatedAt: updatedAt ?? tNow,
);
}

// ── UserKeyEntity ────────────────────────────────────────────────────────────

UserKeyEntity aUserKey({
Expand Down
Loading
Loading