Repository navigation
Expand file tree
/
Copy pathsettlements.test.js
More file actions
137 lines (120 loc) · 4.44 KB
/
Copy pathsettlements.test.js
File metadata and controls
137 lines (120 loc) · 4.44 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
import { afterAll, beforeAll, beforeEach, describe, it } from "vitest";
import { assertFails, assertSucceeds } from "@firebase/rules-unit-testing";
import {
addDoc,
collection,
deleteDoc,
doc,
getDoc,
serverTimestamp,
setDoc,
updateDoc,
} from "firebase/firestore";
import {
ADMIN,
ALICE,
BOB,
EARLIER,
GONE,
GROUP,
OUTSIDER,
as,
groupDoc,
seed,
setupRules,
} from "./helpers";
const CAROL = "carol";
let env;
beforeAll(async () => {
env = await setupRules();
});
afterAll(() => env.cleanup());
beforeEach(async () => {
await env.clearFirestore();
await seed(env, async (db) => {
await setDoc(doc(db, "groups", GROUP), groupDoc({ members: [ADMIN, ALICE, BOB, CAROL] }));
// Alice paid Bob back, and Carol was the one who logged it.
await setDoc(doc(db, "settlements", "paid"), {
groupId: GROUP,
from: ALICE,
to: BOB,
amount: 100,
createdBy: CAROL,
createdAt: EARLIER,
});
});
});
const settlementRef = (uid, id = "paid") => doc(as(env, uid), "settlements", id);
/** What addSettlement() writes. */
const newSettlement = (overrides = {}) => ({
groupId: GROUP,
from: ALICE,
to: BOB,
amount: 50,
createdBy: ALICE,
createdAt: serverTimestamp(),
...overrides,
});
describe("reading settlements", () => {
it("is allowed to members and refused to outsiders", async () => {
await assertSucceeds(getDoc(settlementRef(BOB)));
await assertFails(getDoc(settlementRef(OUTSIDER)));
});
});
describe("recording a repayment", () => {
it("allows it the way the app writes it", async () => {
await assertSucceeds(addDoc(collection(as(env, ALICE), "settlements"), newSettlement()));
});
// Whoever is holding the phone when the cash changes hands logs it.
it("allows a member to record one between two other members", async () => {
await assertSucceeds(
addDoc(collection(as(env, CAROL), "settlements"), newSettlement({ createdBy: CAROL }))
);
});
it("refuses it to anyone outside the group", async () => {
await assertFails(
addDoc(collection(as(env, OUTSIDER), "settlements"), newSettlement({ createdBy: OUTSIDER }))
);
});
it("refuses recording it under someone else's name", async () => {
await assertFails(addDoc(collection(as(env, BOB), "settlements"), newSettlement()));
});
it("refuses paying yourself, non-members and non-positive amounts", async () => {
const db = as(env, ALICE);
await assertFails(addDoc(collection(db, "settlements"), newSettlement({ to: ALICE })));
await assertFails(addDoc(collection(db, "settlements"), newSettlement({ to: GONE })));
await assertFails(addDoc(collection(db, "settlements"), newSettlement({ from: GONE })));
await assertFails(addDoc(collection(db, "settlements"), newSettlement({ amount: 0 })));
await assertFails(addDoc(collection(db, "settlements"), newSettlement({ amount: "50" })));
});
});
describe("changing a repayment", () => {
it("is never allowed; delete and record it again instead", async () => {
await assertFails(updateDoc(settlementRef(CAROL), { amount: 1 }));
await assertFails(updateDoc(settlementRef(ADMIN), { amount: 1 }));
});
});
describe("undoing a repayment", () => {
it("is allowed to whoever logged it", async () => {
await assertSucceeds(deleteDoc(settlementRef(CAROL)));
});
// Otherwise a member could log a repayment they never made, wiping their
// own debt, and the person supposedly paid couldn't take it back.
it("is allowed to either side of the payment", async () => {
await assertSucceeds(deleteDoc(settlementRef(BOB)));
});
it("is allowed to the payer too", async () => {
await assertSucceeds(deleteDoc(settlementRef(ALICE)));
});
it("is allowed to the admin", async () => {
await assertSucceeds(deleteDoc(settlementRef(ADMIN)));
});
it("is refused to uninvolved members and outsiders", async () => {
// Dave is in the group but had nothing to do with this payment.
await seed(env, (db) =>
setDoc(doc(db, "groups", GROUP), groupDoc({ members: [ADMIN, ALICE, BOB, CAROL, "dave"] }))
);
await assertFails(deleteDoc(settlementRef("dave")));
await assertFails(deleteDoc(settlementRef(OUTSIDER)));
});
});