Skip to content

Commit 8609f28

Browse files
committed
Unify the Clarity tests in a single .tests file
This commit refactors the tool to search for both invariants and property tests within a single Clarity test file. - Rendezvous databuilders and utilities are now moved to shared. - Related tests have also been relocated to shared tests. - Unreliable tests have been removed.
1 parent 4411604 commit 8609f28

14 files changed

+431
-689
lines changed

example/contracts/cargo.invariants.clar

Lines changed: 0 additions & 9 deletions
This file was deleted.

example/contracts/cargo.tests.clar

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
;; Invariants
2+
3+
(define-read-only (invariant-last-shipment-id-gt-0-after-create-shipment)
4+
(let
5+
(
6+
(create-shipment-num-calls
7+
(default-to u0 (get called (map-get? context "create-new-shipment"))))
8+
)
9+
(if (> create-shipment-num-calls u0)
10+
(> (var-get last-shipment-id) u0)
11+
true)))
12+
13+
;; Properties
14+
115
;; Constants for errors
216
(define-constant ERR_ASSERTION_FAILED (err 1))
317
(define-constant ERR_CONTRACT_CALL_FAILED (err 2))

example/contracts/counter.invariants.clar

Lines changed: 0 additions & 11 deletions
This file was deleted.

example/contracts/counter.tests.clar

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
;; The idea of having tests side by side to the contract is to ensure that
2+
;; they are treated as first-class citizens.
3+
;; Each invariant is a read-only function that starts with "invariant-".
4+
;; Each property test is a public function that starts with "test-".
5+
6+
;; Invariants
7+
8+
(define-read-only (invariant-counter-gt-zero)
9+
(let
10+
((increment-num-calls (default-to u0 (get called (map-get? context "increment"))))
11+
(decrement-num-calls (default-to u0 (get called (map-get? context "decrement")))))
12+
(if (> increment-num-calls decrement-num-calls)
13+
(> (var-get counter) u0)
14+
true)))
15+
16+
;; Properties
17+
118
;; This test catches the bug in the counter contract.
219
(define-public (test-increment)
320
(let ((counter-before (get-counter)))

example/contracts/reverse.invariants.clar

Lines changed: 0 additions & 3 deletions
This file was deleted.

example/contracts/reverse.tests.clar

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
;; Invariants
2+
3+
;; Properties
4+
15
(define-constant ERR_ASSERTION_FAILED (err 1))
26

37
(define-public (test-reverse (seq (list 127 int)))

example/contracts/slice.invariants.clar

Lines changed: 0 additions & 3 deletions
This file was deleted.

example/contracts/slice.tests.clar

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
;; Invariants
2+
3+
;; Properties
4+
15
(define-constant ERR_CONTRACT_CALL_FAILED (err 0))
26
(define-constant ERR_ASSERTION_FAILED_1 (err 1))
37
(define-constant ERR_ASSERTION_FAILED_2 (err 2))

invariant.tests.ts

Lines changed: 4 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
import { initSimnet } from "@hirosystems/clarinet-sdk";
2+
import { initializeClarityContext, initializeLocalContext } from "./invariant";
23
import {
34
buildRendezvousData,
45
deployRendezvous,
5-
deriveRendezvousName,
66
filterRendezvousInterfaces,
7-
getContractNameFromRendezvousId,
8-
getInvariantContractSource,
9-
initializeClarityContext,
10-
initializeLocalContext,
11-
scheduleRendezvous,
12-
} from "./invariant";
13-
import {
147
getFunctionsFromContractInterfaces,
15-
getSimnetContractSource,
168
getSimnetDeployerContractsInterfaces,
9+
getTestContractSource,
1710
} from "./shared";
1811
import { resolve } from "path";
1912
import fs from "fs";
20-
import fc from "fast-check";
2113
import { Cl } from "@stacks/transactions";
2214

2315
describe("File stream operations", () => {
@@ -30,15 +22,15 @@ describe("File stream operations", () => {
3022
const sutContractsList = Array.from(sutContractsInterfaces.keys());
3123
const expectedInvariantContractSources = sutContractsList.map(
3224
(contractId) => {
33-
const invariantContractName = `${contractId.split(".")[1]}.invariants`;
25+
const invariantContractName = `${contractId.split(".")[1]}.tests`;
3426
const invariantContractPath = `${contractsPath}/${invariantContractName}.clar`;
3527
return fs.readFileSync(invariantContractPath).toString();
3628
}
3729
);
3830

3931
// Act
4032
const actualInvariantContractSources = sutContractsList.map((contractId) =>
41-
getInvariantContractSource(contractsPath, contractId)
33+
getTestContractSource(contractsPath, contractId)
4234
);
4335

4436
// Assert
@@ -49,115 +41,6 @@ describe("File stream operations", () => {
4941
});
5042

5143
describe("Simnet contracts operations", () => {
52-
it("retrieves Rendezvous contracts data", async () => {
53-
// Arrange
54-
const manifestPath = resolve(__dirname, "./example/Clarinet.toml");
55-
const contractsPath = resolve(__dirname, "./example/contracts");
56-
const simnet = await initSimnet(manifestPath);
57-
const sutContractsInterfaces = getSimnetDeployerContractsInterfaces(simnet);
58-
const sutContractsList = Array.from(sutContractsInterfaces.keys());
59-
60-
const expectedRendezvousData = sutContractsList.map((contractId) => {
61-
const sutContractSource = getSimnetContractSource(simnet, contractId);
62-
const invariantContractSource = getInvariantContractSource(
63-
contractsPath,
64-
contractId
65-
);
66-
const rendezvousSource = scheduleRendezvous(
67-
sutContractSource!,
68-
invariantContractSource
69-
);
70-
const rendezvousName = deriveRendezvousName(contractId);
71-
72-
return {
73-
rendezvousName,
74-
rendezvousSource,
75-
rendezvousContractId: `${simnet.deployer}.${rendezvousName}`,
76-
};
77-
});
78-
79-
// Act
80-
const actualRendezvousData = sutContractsList.map((contractId) =>
81-
buildRendezvousData(simnet, contractId, contractsPath)
82-
);
83-
84-
// Assert
85-
expect(actualRendezvousData).toEqual(expectedRendezvousData);
86-
});
87-
88-
it("deploys Rendezvous contracts to the simnet", async () => {
89-
// Arrange
90-
const manifestPath = resolve(__dirname, "./example/Clarinet.toml");
91-
const contractsPath = resolve(__dirname, "./example/contracts");
92-
const simnet = await initSimnet(manifestPath);
93-
const sutContractsInterfaces = getSimnetDeployerContractsInterfaces(simnet);
94-
const sutContractsList = Array.from(sutContractsInterfaces.keys());
95-
const rendezvousData = sutContractsList.map((contractId) =>
96-
buildRendezvousData(simnet, contractId, contractsPath)
97-
);
98-
99-
// Act
100-
rendezvousData.forEach((contractData) => {
101-
deployRendezvous(
102-
simnet,
103-
contractData.rendezvousName,
104-
contractData.rendezvousSource
105-
);
106-
});
107-
108-
// Re-fetch contract interfaces to check after deployment
109-
const actualSimnetContractsInterfacesAfterDeploy =
110-
getSimnetDeployerContractsInterfaces(simnet);
111-
const actualSimnetContractsListAfterDeploy = Array.from(
112-
actualSimnetContractsInterfacesAfterDeploy.keys()
113-
);
114-
115-
// Assert
116-
// Check if all expected Rendezvous contracts are present in the result
117-
rendezvousData.forEach((contractData) => {
118-
expect(actualSimnetContractsListAfterDeploy).toContain(
119-
contractData.rendezvousContractId
120-
);
121-
});
122-
123-
// Ensure there are exactly double the number of original contracts (pre-deployment and Rendezvous)
124-
expect(actualSimnetContractsListAfterDeploy).toHaveLength(
125-
2 * sutContractsList.length
126-
);
127-
});
128-
129-
it("correctly filters the Rendezvous contracts interfaces", async () => {
130-
// Arrange
131-
const manifestPath = resolve(__dirname, "./example/Clarinet.toml");
132-
const contractsPath = resolve(__dirname, "./example/contracts");
133-
const simnet = await initSimnet(manifestPath);
134-
const sutContractsInterfaces = getSimnetDeployerContractsInterfaces(simnet);
135-
const sutContractsList = Array.from(sutContractsInterfaces.keys());
136-
const expectedRendezvousList = sutContractsList
137-
.map((contractId) =>
138-
buildRendezvousData(simnet, contractId, contractsPath)
139-
)
140-
.map((contractData) => {
141-
deployRendezvous(
142-
simnet,
143-
contractData.rendezvousName,
144-
contractData.rendezvousSource
145-
);
146-
return contractData.rendezvousContractId;
147-
})
148-
.sort();
149-
150-
// Act
151-
const actualRendezvousList = Array.from(
152-
filterRendezvousInterfaces(
153-
getSimnetDeployerContractsInterfaces(simnet)
154-
).keys()
155-
).sort();
156-
157-
// Assert
158-
expect(actualRendezvousList).toEqual(expectedRendezvousList);
159-
});
160-
16144
it("correctly initializes the local context for a given functions map", async () => {
16245
// Arrange
16346
const manifestPath = resolve(__dirname, "./example/Clarinet.toml");
@@ -243,89 +126,4 @@ describe("Simnet contracts operations", () => {
243126
// Assert
244127
expect(actualContext).toEqual(expectedContext);
245128
});
246-
247-
it("retrieves the contract source from the simnet", async () => {
248-
// Arrange
249-
const manifestPath = resolve(__dirname, "./example/Clarinet.toml");
250-
const simnet = await initSimnet(manifestPath);
251-
const sutContractsInterfaces = getSimnetDeployerContractsInterfaces(simnet);
252-
const sutContractsList = Array.from(sutContractsInterfaces.keys());
253-
254-
const expectedContractSources = sutContractsList.map((contractId) =>
255-
simnet.getContractSource(contractId)
256-
);
257-
258-
// Act
259-
const actualContractSources = sutContractsList.map((contractId) =>
260-
getSimnetContractSource(simnet, contractId)
261-
);
262-
263-
// Assert
264-
expect(actualContractSources).toEqual(expectedContractSources);
265-
});
266-
});
267-
268-
describe("Successfully schedules rendez-vous", () => {
269-
const context = `(define-map context (string-ascii 100) {
270-
called: uint
271-
;; other data
272-
})
273-
274-
(define-public (update-context (function-name (string-ascii 100)) (called uint))
275-
(ok (map-set context function-name {called: called})))`;
276-
277-
it("adds context between contract and invariants", () => {
278-
fc.assert(
279-
// Arrange
280-
fc.property(fc.string(), fc.string(), (contract, invariants) => {
281-
// Act
282-
const actual = scheduleRendezvous(contract, invariants);
283-
// Assert
284-
const expected = `${contract}\n\n${context}\n\n${invariants}`;
285-
expect(actual).toBe(expected);
286-
})
287-
);
288-
});
289-
290-
it("derives the Rendezvous contract name", () => {
291-
const addressCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
292-
const contractNameCharset =
293-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
294-
fc.assert(
295-
// Arrange
296-
fc.property(
297-
fc.stringOf(fc.constantFrom(...addressCharset)),
298-
fc.stringOf(fc.constantFrom(...contractNameCharset)),
299-
(address, contractName) => {
300-
// Act
301-
const actual = deriveRendezvousName(`${address}.${contractName}`);
302-
// Assert
303-
const expected = `${contractName}_rendezvous`;
304-
expect(actual).toBe(expected);
305-
}
306-
)
307-
);
308-
});
309-
310-
it("gets contract name from Rendezvous contract name", () => {
311-
const addressCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
312-
const contractNameCharset =
313-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
314-
fc.assert(
315-
// Arrange
316-
fc.property(
317-
fc.stringOf(fc.constantFrom(...addressCharset)),
318-
fc.stringOf(fc.constantFrom(...contractNameCharset)),
319-
(address, contractName) => {
320-
const rendezvousId = `${address}.${contractName}_rendezvous`;
321-
322-
// Act
323-
const actual = getContractNameFromRendezvousId(rendezvousId);
324-
325-
// Assert
326-
expect(actual).toBe(contractName);
327-
}
328-
)
329-
);
330-
});
331129
});

0 commit comments

Comments
 (0)