Skip to content

Commit d6e0a74

Browse files
authored
Merge pull request #276 from Meme11030/feat/transaction-module-tests
test(transactions): add unit tests for service, controller, and domai…
2 parents 230e60c + 5661adc commit d6e0a74

1 file changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {
2+
TransactionStatus,
3+
createTransaction,
4+
canTransitionTransactionStatus,
5+
transitionTransactionStatus,
6+
Transaction,
7+
} from './transaction.model';
8+
9+
describe('transaction.model', () => {
10+
describe('createTransaction', () => {
11+
it('creates a PENDING transaction with required fields', () => {
12+
const tx = createTransaction('50', { type: 'NATIVE' }, 'wallet-a');
13+
14+
expect(tx.status).toBe(TransactionStatus.PENDING);
15+
expect(tx.amount).toBe('50');
16+
expect(tx.asset).toEqual({ type: 'NATIVE' });
17+
expect(tx.senderWalletId).toBe('wallet-a');
18+
expect(tx.receiverWalletId).toBeNull();
19+
expect(tx.stellarRefs).toEqual({});
20+
expect(tx.submittedAt).toBeNull();
21+
expect(tx.confirmedAt).toBeNull();
22+
expect(tx.failedAt).toBeNull();
23+
expect(tx.metadata).toBeNull();
24+
expect(tx.id).toBeDefined();
25+
});
26+
27+
it('uses provided id and timestamp', () => {
28+
const at = new Date('2024-01-01');
29+
const tx = createTransaction('10', { type: 'NATIVE' }, 'wallet-a', null, null, 'fixed-id', at);
30+
31+
expect(tx.id).toBe('fixed-id');
32+
expect(tx.createdAt).toBe(at);
33+
expect(tx.updatedAt).toBe(at);
34+
expect(tx.statusChangedAt).toBe(at);
35+
});
36+
37+
it('sets receiverWalletId and metadata when provided', () => {
38+
const tx = createTransaction('10', { type: 'NATIVE' }, 'wallet-a', 'wallet-b', { ref: '123' });
39+
40+
expect(tx.receiverWalletId).toBe('wallet-b');
41+
expect(tx.metadata).toEqual({ ref: '123' });
42+
});
43+
});
44+
45+
describe('canTransitionTransactionStatus', () => {
46+
it.each([
47+
[TransactionStatus.PENDING, TransactionStatus.SUBMITTED, true],
48+
[TransactionStatus.PENDING, TransactionStatus.FAILED, true],
49+
[TransactionStatus.SUBMITTED, TransactionStatus.CONFIRMED, true],
50+
[TransactionStatus.SUBMITTED, TransactionStatus.FAILED, true],
51+
[TransactionStatus.CONFIRMED, TransactionStatus.FAILED, false],
52+
[TransactionStatus.FAILED, TransactionStatus.PENDING, false],
53+
[TransactionStatus.CONFIRMED, TransactionStatus.PENDING, false],
54+
[TransactionStatus.PENDING, TransactionStatus.CONFIRMED, false],
55+
])('%s -> %s is %s', (from, to, expected) => {
56+
expect(canTransitionTransactionStatus(from, to)).toBe(expected);
57+
});
58+
});
59+
60+
describe('transitionTransactionStatus', () => {
61+
let pending: Transaction;
62+
63+
beforeEach(() => {
64+
pending = createTransaction('100', { type: 'NATIVE' }, 'wallet-a', 'wallet-b');
65+
});
66+
67+
it('transitions PENDING -> SUBMITTED and sets submittedAt', () => {
68+
const at = new Date('2024-06-01');
69+
const result = transitionTransactionStatus(pending, TransactionStatus.SUBMITTED, undefined, undefined, at);
70+
71+
expect(result.status).toBe(TransactionStatus.SUBMITTED);
72+
expect(result.submittedAt).toBe(at);
73+
expect(result.statusChangedAt).toBe(at);
74+
expect(result.updatedAt).toBe(at);
75+
});
76+
77+
it('transitions SUBMITTED -> CONFIRMED and sets confirmedAt', () => {
78+
const submitted = transitionTransactionStatus(pending, TransactionStatus.SUBMITTED);
79+
const at = new Date('2024-06-02');
80+
const result = transitionTransactionStatus(submitted, TransactionStatus.CONFIRMED, undefined, undefined, at);
81+
82+
expect(result.status).toBe(TransactionStatus.CONFIRMED);
83+
expect(result.confirmedAt).toBe(at);
84+
});
85+
86+
it('transitions PENDING -> FAILED and sets failedAt', () => {
87+
const at = new Date('2024-06-01');
88+
const result = transitionTransactionStatus(pending, TransactionStatus.FAILED, 'timeout', undefined, at);
89+
90+
expect(result.status).toBe(TransactionStatus.FAILED);
91+
expect(result.failedAt).toBe(at);
92+
expect(result.statusReason).toBe('timeout');
93+
});
94+
95+
it('merges stellarRefs when provided', () => {
96+
const result = transitionTransactionStatus(
97+
pending,
98+
TransactionStatus.SUBMITTED,
99+
undefined,
100+
{ hash: 'abc123', ledger: 42 },
101+
);
102+
103+
expect(result.stellarRefs.hash).toBe('abc123');
104+
expect(result.stellarRefs.ledger).toBe(42);
105+
});
106+
107+
it('throws on invalid transition', () => {
108+
const confirmed = transitionTransactionStatus(
109+
transitionTransactionStatus(pending, TransactionStatus.SUBMITTED),
110+
TransactionStatus.CONFIRMED,
111+
);
112+
113+
expect(() =>
114+
transitionTransactionStatus(confirmed, TransactionStatus.PENDING),
115+
).toThrow('Invalid transaction status transition');
116+
});
117+
118+
it('returns same object when transitioning to same status', () => {
119+
const result = transitionTransactionStatus(pending, TransactionStatus.PENDING);
120+
121+
expect(result).toBe(pending);
122+
});
123+
124+
it('does not mutate the original transaction', () => {
125+
const originalStatus = pending.status;
126+
transitionTransactionStatus(pending, TransactionStatus.SUBMITTED);
127+
128+
expect(pending.status).toBe(originalStatus);
129+
});
130+
});
131+
});

0 commit comments

Comments
 (0)