A social knowledge tool for researchers built on ATProto
1import { Result, ok, err } from '../../../../shared/core/Result';
2import { ICardRepository } from '../../domain/ICardRepository';
3import { Card } from '../../domain/Card';
4import { CardId } from '../../domain/value-objects/CardId';
5import { CuratorId } from '../../domain/value-objects/CuratorId';
6import { URL } from '../../domain/value-objects/URL';
7
8export class InMemoryCardRepository implements ICardRepository {
9 private cards: Map<string, Card> = new Map();
10 private shouldFail: boolean = false;
11 private shouldFailSave: boolean = false;
12
13 private clone(card: Card): Card {
14 // Simple clone - in a real implementation you'd want proper deep cloning
15 const cardResult = Card.create(
16 {
17 curatorId: card.props.curatorId,
18 type: card.type,
19 content: card.content,
20 parentCardId: card.parentCardId,
21 url: card.url,
22 publishedRecordId: card.publishedRecordId,
23 libraryMemberships: card.libraryMemberships,
24 libraryCount: card.libraryCount,
25 createdAt: card.createdAt,
26 updatedAt: card.updatedAt,
27 },
28 card.id,
29 );
30
31 if (cardResult.isErr()) {
32 throw new Error(`Failed to clone card: ${cardResult.error.message}`);
33 }
34
35 return cardResult.value;
36 }
37
38 async findById(id: CardId): Promise<Result<Card | null>> {
39 if (this.shouldFail) {
40 return err(new Error('Simulated find failure'));
41 }
42 try {
43 const card = this.cards.get(id.getStringValue());
44 return ok(card ? this.clone(card) : null);
45 } catch (error) {
46 return err(error as Error);
47 }
48 }
49
50 async findUsersUrlCardByUrl(
51 url: URL,
52 curatorId: CuratorId,
53 ): Promise<Result<Card | null>> {
54 try {
55 const card = Array.from(this.cards.values()).find(
56 (card) =>
57 card.type.value === 'URL' &&
58 card.url?.value === url.value &&
59 card.props.curatorId.equals(curatorId),
60 );
61 return ok(card ? this.clone(card) : null);
62 } catch (error) {
63 return err(error as Error);
64 }
65 }
66
67 async save(card: Card): Promise<Result<void>> {
68 if (this.shouldFailSave || this.shouldFail) {
69 return err(new Error('Simulated save failure'));
70 }
71 try {
72 this.cards.set(card.cardId.getStringValue(), this.clone(card));
73 return ok(undefined);
74 } catch (error) {
75 return err(error as Error);
76 }
77 }
78
79 async delete(cardId: CardId): Promise<Result<void>> {
80 try {
81 this.cards.delete(cardId.getStringValue());
82 return ok(undefined);
83 } catch (error) {
84 return err(error as Error);
85 }
86 }
87
88 setShouldFail(shouldFail: boolean): void {
89 this.shouldFail = shouldFail;
90 }
91
92 setShouldFailSave(shouldFailSave: boolean): void {
93 this.shouldFailSave = shouldFailSave;
94 }
95
96 // Helper methods for testing
97 public clear(): void {
98 this.cards.clear();
99 this.shouldFail = false;
100 this.shouldFailSave = false;
101 }
102
103 public getStoredCard(id: CardId): Card | undefined {
104 return this.cards.get(id.getStringValue());
105 }
106
107 public getAllCards(): Card[] {
108 return Array.from(this.cards.values()).map((card) => this.clone(card));
109 }
110}