forked from Stellar-split/split-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproperty-client-invariants.test.ts
More file actions
150 lines (139 loc) · 4.37 KB
/
Copy pathproperty-client-invariants.test.ts
File metadata and controls
150 lines (139 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { describe, it, expect } from "vitest";
import * as fc from "fast-check";
import { Keypair } from "@stellar/stellar-sdk";
import {
formatAmount,
parseAmount,
isValidStellarAddress,
deadlineFromDays,
isExpired,
truncateAddress,
} from "../src/utils.js";
import type { InvoiceStatus } from "../src/types.js";
const STATUSES: InvoiceStatus[] = ["Pending", "Released", "Refunded", "Cancelled"];
const VALID_ADDRESSES = Array.from({ length: 50 }, () => Keypair.random().publicKey());
describe("StellarSplitClient sequential call invariants (property-based)", () => {
it("parseAmount is inverse of formatAmount for all valid stroops", () => {
fc.assert(
fc.property(
fc.bigInt({ min: 0n, max: 50_000_000_000_000n }),
(stroops) => {
const formatted = formatAmount(stroops);
const parsed = parseAmount(formatted);
expect(parsed).toBe(stroops);
},
),
{ numRuns: 500 },
);
});
it("formatAmount(parseAmount(s)) is idempotent for 7-decimal strings", () => {
fc.assert(
fc.property(
fc.bigInt({ min: 0n, max: 1_000_000_000_000n }),
(stroops) => {
const formatted = formatAmount(stroops);
const reformatted = formatAmount(parseAmount(formatted));
expect(reformatted).toBe(formatted);
},
),
{ numRuns: 500 },
);
});
it("isValidStellarAddress is consistent across repeated calls", () => {
fc.assert(
fc.property(fc.constantFrom(...VALID_ADDRESSES), (address) => {
const first = isValidStellarAddress(address);
const second = isValidStellarAddress(address);
expect(second).toBe(first);
}),
{ numRuns: 500 },
);
});
it("truncateAddress preserves prefix and suffix", () => {
fc.assert(
fc.property(
fc.constantFrom(...VALID_ADDRESSES),
fc.integer({ min: 1, max: 10 }),
(address, chars) => {
const truncated = truncateAddress(address, chars);
expect(truncated.startsWith(address.slice(0, chars))).toBe(true);
expect(truncated.endsWith(address.slice(-chars))).toBe(true);
expect(truncated).toContain("...");
},
),
{ numRuns: 500 },
);
});
it("truncateAddress returns original string if too short", () => {
fc.assert(
fc.property(
fc.string({ minLength: 1, maxLength: 10 }),
fc.integer({ min: 1, max: 10 }),
(short, chars) => {
fc.pre(short.length <= chars * 2 + 3);
expect(truncateAddress(short, chars)).toBe(short);
},
),
{ numRuns: 500 },
);
});
it("deadlineFromDays is deterministic for same input", () => {
fc.assert(
fc.property(
fc.integer({ min: 1, max: 3650 }),
(days) => {
const first = deadlineFromDays(days);
const second = deadlineFromDays(days);
expect(second).toBe(first);
},
),
{ numRuns: 500 },
);
});
it("isExpired is consistent: if deadline < now, always expired", () => {
fc.assert(
fc.property(
fc.integer({ min: 1, max: 3650 }),
(daysAgo) => {
const now = Math.floor(Date.now() / 1000);
const past = now - daysAgo * 86400;
expect(isExpired(past)).toBe(true);
},
),
{ numRuns: 500 },
);
});
it("all invoice statuses are valid string literals", () => {
for (const status of STATUSES) {
expect(typeof status).toBe("string");
expect(["Pending", "Released", "Refunded", "Cancelled"]).toContain(status);
}
});
it("formatAmount output length is consistent across inputs", () => {
fc.assert(
fc.property(
fc.bigInt({ min: 0n, max: 10_000_000_000n }),
(stroops) => {
const formatted = formatAmount(stroops);
const dotIndex = formatted.indexOf(".");
expect(dotIndex).toBeGreaterThan(0);
expect(formatted.length - dotIndex - 1).toBe(7);
},
),
{ numRuns: 500 },
);
});
it("parseAmount of negative-looking string returns negative bigint", () => {
fc.assert(
fc.property(
fc.bigInt({ min: 1n, max: 1_000_000_000n }),
(positive) => {
const negativeStr = `-${positive}`;
const result = parseAmount(negativeStr);
expect(result).toBeLessThan(0n);
},
),
{ numRuns: 500 },
);
});
});