This repository has no description
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 static instance: InMemoryCardRepository;
10 private cards: Map<string, Card> = new Map();
11 private shouldFail: boolean = false;
12 private shouldFailSave: boolean = false;
13
14 private constructor() {}
15
16 public static getInstance(): InMemoryCardRepository {
17 if (!InMemoryCardRepository.instance) {
18 InMemoryCardRepository.instance = new InMemoryCardRepository();
19 }
20 return InMemoryCardRepository.instance;
21 }
22
23 private clone(card: Card): Card {
24 // Simple clone - in a real implementation you'd want proper deep cloning
25 const cardResult = Card.create(
26 {
27 curatorId: card.props.curatorId,
28 type: card.type,
29 content: card.content,
30 parentCardId: card.parentCardId,
31 viaCardId: card.viaCardId,
32 url: card.url,
33 publishedRecordId: card.publishedRecordId,
34 libraryMemberships: card.libraryMemberships,
35 libraryCount: card.libraryCount,
36 createdAt: card.createdAt,
37 updatedAt: card.updatedAt,
38 },
39 card.id,
40 );
41
42 if (cardResult.isErr()) {
43 throw new Error(`Failed to clone card: ${cardResult.error.message}`);
44 }
45
46 return cardResult.value;
47 }
48
49 async findById(id: CardId): Promise<Result<Card | null>> {
50 if (this.shouldFail) {
51 return err(new Error('Simulated find failure'));
52 }
53 try {
54 const card = this.cards.get(id.getStringValue());
55 return ok(card ? this.clone(card) : null);
56 } catch (error) {
57 return err(error as Error);
58 }
59 }
60
61 async findUsersUrlCardByUrl(
62 url: URL,
63 curatorId: CuratorId,
64 ): Promise<Result<Card | null>> {
65 try {
66 const card = Array.from(this.cards.values()).find(
67 (card) =>
68 card.type.value === 'URL' &&
69 card.url?.value === url.value &&
70 card.props.curatorId.equals(curatorId),
71 );
72 return ok(card ? this.clone(card) : null);
73 } catch (error) {
74 return err(error as Error);
75 }
76 }
77
78 async findUsersNoteCardByUrl(
79 url: URL,
80 curatorId: CuratorId,
81 ): Promise<Result<Card | null>> {
82 try {
83 const card = Array.from(this.cards.values()).find(
84 (card) =>
85 card.type.value === 'NOTE' &&
86 card.url?.value === url.value &&
87 card.props.curatorId.equals(curatorId),
88 );
89 return ok(card ? this.clone(card) : null);
90 } catch (error) {
91 return err(error as Error);
92 }
93 }
94
95 async save(card: Card): Promise<Result<void>> {
96 if (this.shouldFailSave || this.shouldFail) {
97 return err(new Error('Simulated save failure'));
98 }
99 try {
100 this.cards.set(card.cardId.getStringValue(), this.clone(card));
101 return ok(undefined);
102 } catch (error) {
103 return err(error as Error);
104 }
105 }
106
107 async delete(cardId: CardId): Promise<Result<void>> {
108 try {
109 this.cards.delete(cardId.getStringValue());
110 return ok(undefined);
111 } catch (error) {
112 return err(error as Error);
113 }
114 }
115
116 setShouldFail(shouldFail: boolean): void {
117 this.shouldFail = shouldFail;
118 }
119
120 setShouldFailSave(shouldFailSave: boolean): void {
121 this.shouldFailSave = shouldFailSave;
122 }
123
124 // Helper methods for testing
125 public clear(): void {
126 this.cards.clear();
127 this.shouldFail = false;
128 this.shouldFailSave = false;
129 }
130
131 public getStoredCard(id: CardId): Card | undefined {
132 return this.cards.get(id.getStringValue());
133 }
134
135 public getAllCards(): Card[] {
136 return Array.from(this.cards.values()).map((card) => this.clone(card));
137 }
138}