@bash-app/bash-common 30.224.0 → 30.226.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/agePolicy.test.d.ts +2 -0
- package/dist/__tests__/agePolicy.test.d.ts.map +1 -0
- package/dist/__tests__/agePolicy.test.js +203 -0
- package/dist/__tests__/agePolicy.test.js.map +1 -0
- package/dist/__tests__/getDisplayGender.test.d.ts +2 -0
- package/dist/__tests__/getDisplayGender.test.d.ts.map +1 -0
- package/dist/__tests__/getDisplayGender.test.js +34 -0
- package/dist/__tests__/getDisplayGender.test.js.map +1 -0
- package/dist/__tests__/statusEnums.test.d.ts +2 -0
- package/dist/__tests__/statusEnums.test.d.ts.map +1 -0
- package/dist/__tests__/statusEnums.test.js +24 -0
- package/dist/__tests__/statusEnums.test.js.map +1 -0
- package/dist/aiApproval.d.ts +64 -0
- package/dist/aiApproval.d.ts.map +1 -0
- package/dist/aiApproval.js +54 -0
- package/dist/aiApproval.js.map +1 -0
- package/dist/definitions.d.ts +44 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +106 -2
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +237 -104
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +12 -2
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/legalTemplates.d.ts +1 -1
- package/dist/legalTemplates.js +1 -1
- package/dist/utils/__tests__/badgeUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/badgeUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/badgeUtils.test.js +157 -0
- package/dist/utils/__tests__/badgeUtils.test.js.map +1 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.js +114 -0
- package/dist/utils/__tests__/bashPointsPaymentUtils.test.js.map +1 -0
- package/dist/utils/__tests__/contentFilterUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/contentFilterUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/contentFilterUtils.test.js +443 -0
- package/dist/utils/__tests__/contentFilterUtils.test.js.map +1 -0
- package/dist/utils/__tests__/displayDateUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/displayDateUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/displayDateUtils.test.js +105 -0
- package/dist/utils/__tests__/displayDateUtils.test.js.map +1 -0
- package/dist/utils/__tests__/getBackendHost.test.d.ts +2 -0
- package/dist/utils/__tests__/getBackendHost.test.d.ts.map +1 -0
- package/dist/utils/__tests__/getBackendHost.test.js +104 -0
- package/dist/utils/__tests__/getBackendHost.test.js.map +1 -0
- package/dist/utils/__tests__/getFrontendHost.test.d.ts +2 -0
- package/dist/utils/__tests__/getFrontendHost.test.d.ts.map +1 -0
- package/dist/utils/__tests__/getFrontendHost.test.js +207 -0
- package/dist/utils/__tests__/getFrontendHost.test.js.map +1 -0
- package/dist/utils/__tests__/promoCodesUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/promoCodesUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/promoCodesUtils.test.js +183 -0
- package/dist/utils/__tests__/promoCodesUtils.test.js.map +1 -0
- package/dist/utils/__tests__/qrCodeUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/qrCodeUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/qrCodeUtils.test.js +99 -0
- package/dist/utils/__tests__/qrCodeUtils.test.js.map +1 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.js +75 -0
- package/dist/utils/__tests__/stripeAccountUtils.test.js.map +1 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts +2 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts.map +1 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js +233 -0
- package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js.map +1 -0
- package/dist/utils/blog/blogCommentDbUtils.d.ts +161 -0
- package/dist/utils/blog/blogCommentDbUtils.d.ts.map +1 -0
- package/dist/utils/blog/blogCommentDbUtils.js +111 -0
- package/dist/utils/blog/blogCommentDbUtils.js.map +1 -0
- package/dist/utils/blog/blogDbUtils.d.ts +10 -0
- package/dist/utils/blog/blogDbUtils.d.ts.map +1 -1
- package/dist/utils/blog/blogDbUtils.js +71 -0
- package/dist/utils/blog/blogDbUtils.js.map +1 -1
- package/dist/utils/urlUtils.d.ts +10 -0
- package/dist/utils/urlUtils.d.ts.map +1 -1
- package/dist/utils/urlUtils.js +24 -5
- package/dist/utils/urlUtils.js.map +1 -1
- package/dist/utils/userUtils.d.ts +32 -0
- package/dist/utils/userUtils.d.ts.map +1 -1
- package/dist/utils/userUtils.js +27 -0
- package/dist/utils/userUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +951 -845
- package/src/__tests__/agePolicy.test.ts +257 -0
- package/src/__tests__/getDisplayGender.test.ts +50 -0
- package/src/__tests__/statusEnums.test.ts +36 -0
- package/src/definitions.ts +138 -1
- package/src/extendedSchemas.ts +41 -2
- package/src/index.ts +1 -0
- package/src/legalTemplates.ts +1 -1
- package/src/utils/__tests__/badgeUtils.test.ts +191 -0
- package/src/utils/__tests__/bashPointsPaymentUtils.test.ts +153 -0
- package/src/utils/__tests__/contentFilterUtils.test.ts +547 -0
- package/src/utils/__tests__/displayDateUtils.test.ts +113 -0
- package/src/utils/__tests__/getBackendHost.test.ts +128 -0
- package/src/utils/__tests__/getFrontendHost.test.ts +232 -0
- package/src/utils/__tests__/promoCodesUtils.test.ts +237 -0
- package/src/utils/__tests__/qrCodeUtils.test.ts +143 -0
- package/src/utils/__tests__/stripeAccountUtils.test.ts +93 -0
- package/src/utils/blog/__tests__/getRelatedBlogPosts.test.ts +271 -0
- package/src/utils/blog/blogCommentDbUtils.ts +161 -0
- package/src/utils/blog/blogDbUtils.ts +95 -1
- package/src/utils/urlUtils.ts +27 -5
- package/src/utils/userUtils.ts +40 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* badgeUtils — host level / temp-badge helpers.
|
|
3
|
+
*
|
|
4
|
+
* The level-badge calculation is the public reputation signal on every host
|
|
5
|
+
* profile. A bug here either over- or under-credits a host (= unfair / loss
|
|
6
|
+
* of trust). We pin the actual ladder defined in DEFAULT_LEVEL_BADGES:
|
|
7
|
+
*
|
|
8
|
+
* novice → 1+ events
|
|
9
|
+
* regular → 5+ events, rating ≥ 4.0
|
|
10
|
+
* maverick → 15+ events, rating ≥ 4.5
|
|
11
|
+
* legend → 30+ events, rating ≥ 4.8
|
|
12
|
+
*
|
|
13
|
+
* We also pin temp-badge expiry semantics: missing expiresOn = never expires.
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
getLevelBadgeById,
|
|
17
|
+
getTemporaryBadgeById,
|
|
18
|
+
isTemporaryBadgeExpired,
|
|
19
|
+
filterExpiredTemporaryBadges,
|
|
20
|
+
determineLevelBadge,
|
|
21
|
+
} from "../badgeUtils.js";
|
|
22
|
+
import type { UserTemporaryBadge } from "../../definitions.js";
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// getLevelBadgeById / getTemporaryBadgeById — registry lookups
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
describe("getLevelBadgeById", () => {
|
|
29
|
+
it("returns the canonical badge object for a known id", () => {
|
|
30
|
+
const b = getLevelBadgeById("legend");
|
|
31
|
+
expect(b).toBeDefined();
|
|
32
|
+
expect(b?.id).toBe("legend");
|
|
33
|
+
expect(b?.requiredEvents).toBe(30);
|
|
34
|
+
expect(b?.requiredRating).toBe(4.8);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns undefined for an unknown id", () => {
|
|
38
|
+
expect(getLevelBadgeById("definitely-not-a-badge")).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("returns undefined for empty string", () => {
|
|
42
|
+
expect(getLevelBadgeById("")).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("getTemporaryBadgeById", () => {
|
|
47
|
+
it("returns a known temp badge", () => {
|
|
48
|
+
expect(getTemporaryBadgeById("trendingStar")?.id).toBe("trendingStar");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("returns undefined for an unknown id", () => {
|
|
52
|
+
expect(getTemporaryBadgeById("nope")).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// isTemporaryBadgeExpired
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
describe("isTemporaryBadgeExpired", () => {
|
|
61
|
+
it("returns false when expiresOn is missing (badge never expires)", () => {
|
|
62
|
+
const badge: UserTemporaryBadge = {
|
|
63
|
+
badgeId: "earlyAdopter",
|
|
64
|
+
earnedOn: "2020-01-01T00:00:00Z",
|
|
65
|
+
};
|
|
66
|
+
expect(isTemporaryBadgeExpired(badge)).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("returns true for a clearly past expiresOn", () => {
|
|
70
|
+
const badge: UserTemporaryBadge = {
|
|
71
|
+
badgeId: "trendingStar",
|
|
72
|
+
earnedOn: "2020-01-01T00:00:00Z",
|
|
73
|
+
expiresOn: "2020-02-01T00:00:00Z",
|
|
74
|
+
};
|
|
75
|
+
expect(isTemporaryBadgeExpired(badge)).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("returns false for a clearly future expiresOn", () => {
|
|
79
|
+
const future = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
|
|
80
|
+
.toISOString();
|
|
81
|
+
const badge: UserTemporaryBadge = {
|
|
82
|
+
badgeId: "topHost",
|
|
83
|
+
earnedOn: new Date().toISOString(),
|
|
84
|
+
expiresOn: future,
|
|
85
|
+
};
|
|
86
|
+
expect(isTemporaryBadgeExpired(badge)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns true when expiresOn is RIGHT NOW (boundary: not strictly in the future)", () => {
|
|
90
|
+
// Source uses `expiryDate < currentDate`. A simultaneous tick = false (not expired),
|
|
91
|
+
// but Date.now() inside the function is sampled later than ours — which is enough
|
|
92
|
+
// to flip <. We pin the practical observable: "earlier than now → expired".
|
|
93
|
+
const past = new Date(Date.now() - 1).toISOString();
|
|
94
|
+
const badge: UserTemporaryBadge = {
|
|
95
|
+
badgeId: "topHost",
|
|
96
|
+
earnedOn: "2020-01-01T00:00:00Z",
|
|
97
|
+
expiresOn: past,
|
|
98
|
+
};
|
|
99
|
+
expect(isTemporaryBadgeExpired(badge)).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// filterExpiredTemporaryBadges
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
describe("filterExpiredTemporaryBadges", () => {
|
|
108
|
+
it("returns [] for [] (no surprise allocation)", () => {
|
|
109
|
+
expect(filterExpiredTemporaryBadges([])).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("keeps non-expiring badges", () => {
|
|
113
|
+
const badges: UserTemporaryBadge[] = [
|
|
114
|
+
{ badgeId: "earlyAdopter", earnedOn: "2020-01-01T00:00:00Z" },
|
|
115
|
+
];
|
|
116
|
+
expect(filterExpiredTemporaryBadges(badges)).toHaveLength(1);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("drops expired badges and keeps the rest", () => {
|
|
120
|
+
const future = new Date(Date.now() + 86_400_000).toISOString();
|
|
121
|
+
const past = new Date(Date.now() - 86_400_000).toISOString();
|
|
122
|
+
const badges: UserTemporaryBadge[] = [
|
|
123
|
+
{ badgeId: "topHost", earnedOn: "2020-01-01T00:00:00Z", expiresOn: past },
|
|
124
|
+
{ badgeId: "trendingStar", earnedOn: "2020-01-01T00:00:00Z", expiresOn: future },
|
|
125
|
+
{ badgeId: "earlyAdopter", earnedOn: "2020-01-01T00:00:00Z" },
|
|
126
|
+
];
|
|
127
|
+
const result = filterExpiredTemporaryBadges(badges);
|
|
128
|
+
expect(result.map((b) => b.badgeId)).toEqual(["trendingStar", "earlyAdopter"]);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// determineLevelBadge — the reputation ladder
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
describe("determineLevelBadge — host reputation ladder", () => {
|
|
137
|
+
it("returns null when the host has no events", () => {
|
|
138
|
+
expect(determineLevelBadge(0, 5)).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("returns null when events are below the lowest threshold (1) even with high rating", () => {
|
|
142
|
+
expect(determineLevelBadge(0, 5)).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("awards 'novice' at 1 event with no rating threshold", () => {
|
|
146
|
+
expect(determineLevelBadge(1, 0)).toBe("novice");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("stays at 'novice' between 1 and 4 events", () => {
|
|
150
|
+
expect(determineLevelBadge(2, 4.9)).toBe("novice");
|
|
151
|
+
expect(determineLevelBadge(4, 4.9)).toBe("novice");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("awards 'regular' at 5 events when rating ≥ 4.0", () => {
|
|
155
|
+
expect(determineLevelBadge(5, 4.0)).toBe("regular");
|
|
156
|
+
expect(determineLevelBadge(10, 4.49)).toBe("regular");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("falls BACK to 'novice' when 5+ events but rating < 4.0 (rating gate failed)", () => {
|
|
160
|
+
// Rating gate failed for 'regular', so the next-best the host qualifies for is 'novice'.
|
|
161
|
+
expect(determineLevelBadge(5, 3.9)).toBe("novice");
|
|
162
|
+
expect(determineLevelBadge(20, 3.0)).toBe("novice");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("awards 'maverick' at 15 events when rating ≥ 4.5", () => {
|
|
166
|
+
expect(determineLevelBadge(15, 4.5)).toBe("maverick");
|
|
167
|
+
expect(determineLevelBadge(29, 4.79)).toBe("maverick");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("falls back to 'regular' at 15+ events when rating is between 4.0 and 4.5", () => {
|
|
171
|
+
expect(determineLevelBadge(15, 4.49)).toBe("regular");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("awards 'legend' at 30 events when rating ≥ 4.8", () => {
|
|
175
|
+
expect(determineLevelBadge(30, 4.8)).toBe("legend");
|
|
176
|
+
expect(determineLevelBadge(1000, 5)).toBe("legend");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("falls back to 'maverick' at 30+ events when rating drops below 4.8", () => {
|
|
180
|
+
expect(determineLevelBadge(30, 4.7)).toBe("maverick");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("falls all the way back to 'novice' for a host with many events but a poor rating", () => {
|
|
184
|
+
// 1000 events but rating 1.0 → only the no-rating-gate 'novice' qualifies.
|
|
185
|
+
expect(determineLevelBadge(1000, 1.0)).toBe("novice");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("treats fractional event counts the same as integer (>=)", () => {
|
|
189
|
+
expect(determineLevelBadge(5.5, 4.0)).toBe("regular");
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bashPointsPaymentUtils — earning math + display + deprecated stubs.
|
|
3
|
+
*
|
|
4
|
+
* BashPoints are loyalty cashback. The active code paths are:
|
|
5
|
+
* - calculateBashPointsCashback(amountCents, rate) → integer points
|
|
6
|
+
* - formatBashPoints(n) → "1,000 points"
|
|
7
|
+
*
|
|
8
|
+
* The "service booking" overloads were intentionally deprecated because
|
|
9
|
+
* BashPoints have no fixed dollar value and cannot pay for services. We pin
|
|
10
|
+
* the deprecated stubs here so they stay benign (return safe defaults, log a
|
|
11
|
+
* warning) — accidentally re-enabling them would cause real money loss.
|
|
12
|
+
*/
|
|
13
|
+
import {
|
|
14
|
+
formatBashPoints,
|
|
15
|
+
calculateBashPointsCashback,
|
|
16
|
+
centsToBashPoints,
|
|
17
|
+
bashPointsToCents,
|
|
18
|
+
calculateMaxBashPointsForBooking,
|
|
19
|
+
calculateSplitPayment,
|
|
20
|
+
validateBashPointsPayment,
|
|
21
|
+
} from "../bashPointsPaymentUtils.js";
|
|
22
|
+
|
|
23
|
+
describe("formatBashPoints", () => {
|
|
24
|
+
it("uses singular 'point' for exactly 1", () => {
|
|
25
|
+
expect(formatBashPoints(1)).toBe("1 point");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("uses plural 'points' for 0", () => {
|
|
29
|
+
expect(formatBashPoints(0)).toBe("0 points");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("uses plural for >1", () => {
|
|
33
|
+
expect(formatBashPoints(2)).toBe("2 points");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("inserts en-US thousands separators", () => {
|
|
37
|
+
expect(formatBashPoints(1000)).toBe("1,000 points");
|
|
38
|
+
expect(formatBashPoints(28750)).toBe("28,750 points");
|
|
39
|
+
expect(formatBashPoints(1_000_000)).toBe("1,000,000 points");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("formats negative values (defensive — should never happen but no throw)", () => {
|
|
43
|
+
// -1 !== 1 so it gets the plural suffix
|
|
44
|
+
expect(formatBashPoints(-5)).toBe("-5 points");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("calculateBashPointsCashback", () => {
|
|
49
|
+
it("awards 5% on a $10.00 (1000¢) purchase → 50 points", () => {
|
|
50
|
+
expect(calculateBashPointsCashback(1000, 0.05)).toBe(50);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("awards 0 points on a $0 purchase", () => {
|
|
54
|
+
expect(calculateBashPointsCashback(0, 0.05)).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("awards 0 points when the rate is 0", () => {
|
|
58
|
+
expect(calculateBashPointsCashback(10_000, 0)).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("FLOORS fractional results (never over-awards)", () => {
|
|
62
|
+
// 333 * 0.05 = 16.65 → floor → 16
|
|
63
|
+
expect(calculateBashPointsCashback(333, 0.05)).toBe(16);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("handles a 100% rebate rate", () => {
|
|
67
|
+
expect(calculateBashPointsCashback(2500, 1)).toBe(2500);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("rate > 1 still floors (host can configure bonuses; trust input)", () => {
|
|
71
|
+
expect(calculateBashPointsCashback(100, 1.5)).toBe(150);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("returns 0 (not negative) for negative amounts after floor", () => {
|
|
75
|
+
// Math.floor(-1) = -1; we don't bound the result to >=0 in source,
|
|
76
|
+
// so this pins the actual contract: negative inputs flow through as-is.
|
|
77
|
+
expect(calculateBashPointsCashback(-100, 0.05)).toBe(-5);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("DEPRECATED stubs — must remain safe defaults", () => {
|
|
82
|
+
let warnSpy: jest.SpyInstance;
|
|
83
|
+
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
warnSpy.mockRestore();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("centsToBashPoints", () => {
|
|
93
|
+
it("returns the input as-is and warns", () => {
|
|
94
|
+
expect(centsToBashPoints(1234)).toBe(1234);
|
|
95
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
96
|
+
expect.stringContaining("DEPRECATED: centsToBashPoints")
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("bashPointsToCents", () => {
|
|
102
|
+
it("returns the input as-is and warns", () => {
|
|
103
|
+
expect(bashPointsToCents(500)).toBe(500);
|
|
104
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
105
|
+
expect.stringContaining("DEPRECATED: bashPointsToCents")
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("calculateMaxBashPointsForBooking", () => {
|
|
111
|
+
it("returns 0 (no BashPoints can be applied to a service booking) and warns", () => {
|
|
112
|
+
expect(calculateMaxBashPointsForBooking(10_000, 5_000)).toBe(0);
|
|
113
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
114
|
+
expect.stringContaining("DEPRECATED: calculateMaxBashPointsForBooking")
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("calculateSplitPayment", () => {
|
|
120
|
+
it("forces the full amount onto Stripe (BashPoints contribution = 0) and warns", () => {
|
|
121
|
+
const result = calculateSplitPayment(10_000, 9_999);
|
|
122
|
+
expect(result).toEqual({
|
|
123
|
+
bashPointsCents: 0,
|
|
124
|
+
stripeCents: 10_000,
|
|
125
|
+
bashPointsCredits: 0,
|
|
126
|
+
});
|
|
127
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
128
|
+
expect.stringContaining("DEPRECATED: calculateSplitPayment")
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("preserves the original total on stripeCents even when called with 0", () => {
|
|
133
|
+
const result = calculateSplitPayment(0, 100);
|
|
134
|
+
expect(result.stripeCents).toBe(0);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("validateBashPointsPayment", () => {
|
|
139
|
+
it("ALWAYS returns invalid with a clear redirect-to-Stripe message", () => {
|
|
140
|
+
const result = validateBashPointsPayment(100, 1_000, 500);
|
|
141
|
+
expect(result.valid).toBe(false);
|
|
142
|
+
expect(result.errorMessage).toMatch(/credit card/i);
|
|
143
|
+
expect(result.errorMessage).toMatch(/BashPoints-priced tickets/i);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("rejects even when the user has enough balance (no special-case bypass)", () => {
|
|
147
|
+
// 1_000_000 balance, 100 needed → still rejected because services can't take BashPoints.
|
|
148
|
+
expect(
|
|
149
|
+
validateBashPointsPayment(100, 1_000_000, 100).valid
|
|
150
|
+
).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|