Skip to content
This repository was archived by the owner on Nov 5, 2024. It is now read-only.

Commit 3fc34f3

Browse files
authored
Add getTimestampOffset/setTimestampOffset utilities (#126)
1 parent 268f825 commit 3fc34f3

30 files changed

+571
-227
lines changed

.changeset/long-hairs-collect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@onflow/flow-js-testing": minor
3+
---
4+
5+
Add getTimeOffset/setTimeOffset utilities, allowing to advance the time while testing smart-contracts

cadence/contracts/FlowManager.cdc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub contract FlowManager {
4242

4343
/// Environment Manager
4444
pub event BlockOffsetChanged(offset: UInt64)
45+
pub event TimestampOffsetChanged(offset: UFix64)
4546

4647
pub struct MockBlock {
4748
pub let id: [UInt8; 32]
@@ -62,24 +63,36 @@ pub contract FlowManager {
6263
emit FlowManager.BlockOffsetChanged(offset: offset)
6364
}
6465

66+
pub fun setTimestampOffset(_ offset: UFix64){
67+
self.timestampOffset = offset
68+
emit FlowManager.TimestampOffsetChanged(offset: offset)
69+
}
70+
6571
pub fun getBlockHeight(): UInt64 {
6672
var block = getCurrentBlock()
6773
return block.height + self.blockOffset
6874
}
6975

76+
pub fun getBlockTimestamp(): UFix64 {
77+
var block = getCurrentBlock()
78+
return block.timestamp + self.timestampOffset
79+
}
80+
7081
pub fun getBlock(): MockBlock {
7182
var block = getCurrentBlock()
7283
let mockBlock = MockBlock(block.id, block.height, block.view, block.timestamp);
7384
return mockBlock
7485
}
7586

7687
pub var blockOffset: UInt64;
88+
pub var timestampOffset: UFix64;
7789

7890

7991
// Initialize contract
8092
init(){
8193
// Environment defaults
8294
self.blockOffset = 0;
95+
self.timestampOffset = 0.0;
8396

8497
// Account Manager initialization
8598
let accountManager = Mapper()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import FlowManager from 0x01
2+
3+
pub fun main():UFix64 {
4+
return FlowManager.timestampOffset
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import FlowManager from 0x01
2+
3+
transaction(offset: UFix64){
4+
prepare(signer:AuthAccount){
5+
FlowManager.setTimestampOffset(offset)
6+
}
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import FlowManager from 0x01
2+
3+
pub fun main(): UFix64 {
4+
let mockedTimestamp = FlowManager.getBlockTimestamp();
5+
let realTimestamp = getCurrentBlock().timestamp;
6+
log("Mocked Height: ".concat(mockedTimestamp.toString()))
7+
log("Real Height: ".concat(realTimestamp.toString()))
8+
9+
return mockedTimestamp - realTimestamp;
10+
}

dev-test/utilities.test.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
sendTransaction,
1212
getBlockOffset,
1313
setBlockOffset,
14+
getTimestampOffset,
15+
setTimestampOffset,
1416
} from "../src";
1517
import { extractParameters } from "../src/interaction";
1618
import { importExists, builtInMethods, playgroundImport } from "../src/transformers";
@@ -111,6 +113,100 @@ describe("block height offset utilities", () => {
111113
});
112114
});
113115

116+
describe("timestamp offset", () => {
117+
// Instantiate emulator and path to Cadence files
118+
beforeEach(async () => {
119+
const base = path.resolve(__dirname, "../cadence");
120+
const port = 8085;
121+
await init({ base },);
122+
return emulator.start(port);
123+
});
124+
125+
// Stop emulator, so it could be restarted
126+
afterEach(async () => {
127+
return emulator.stop();
128+
});
129+
130+
it("should return zero offset", async () => {
131+
const [zeroOffset] = await executeScript("get-timestamp-offset");
132+
expect(zeroOffset).toBe("0.00000000");
133+
});
134+
135+
it("should update offset", async () => {
136+
const manager = await getServiceAddress();
137+
const [zeroOffset] = await executeScript("get-timestamp-offset");
138+
expect(zeroOffset).toBe("0.00000000");
139+
140+
const offset = 42;
141+
await shallPass(sendTransaction("set-timestamp-offset", [manager], [offset]));
142+
const [newOffset] = await executeScript("get-timestamp-offset");
143+
expect(newOffset).toBe("42.00000000");
144+
});
145+
146+
it("should read offset with utility method", async () => {
147+
// CadUt version of sending transactions and execution scripts don't have
148+
// import resolver built in, so we need to provide addressMap to it
149+
const FlowManager = await getManagerAddress();
150+
const addressMap = { FlowManager };
151+
152+
const [offSet] = await getTimestampOffset({ addressMap });
153+
154+
expect(offSet).toBe(0);
155+
156+
});
157+
158+
it("should update offset with utility method", async () => {
159+
// CadUt version of sending transactions and execution scripts don't have
160+
// import resolver built in, so we need to provide addressMap to it
161+
const FlowManager = await getManagerAddress();
162+
const addressMap = { FlowManager };
163+
164+
const [oldOffset] = await getTimestampOffset({ addressMap });
165+
166+
expect(oldOffset).toBe(0);
167+
168+
const offset = 42;
169+
170+
const [txResult] = await setTimestampOffset(offset);
171+
expect(txResult.errorMessage).toBe("");
172+
173+
const [newOffset] = await getTimestampOffset({ addressMap });
174+
175+
expect(newOffset).toBe(offset);
176+
});
177+
});
178+
179+
describe("timestamp offset utilities", () => {
180+
// Instantiate emulator and path to Cadence files
181+
beforeEach(async () => {
182+
const base = path.resolve(__dirname, "../cadence");
183+
const port = 8080;
184+
await init({ base }, { port });
185+
return emulator.start(port);
186+
});
187+
188+
// Stop emulator, so it could be restarted
189+
afterEach(async () => {
190+
return emulator.stop();
191+
});
192+
193+
it("should return 0 for initial block offset", async () => {
194+
const [initialOffset] = await shallResolve(manager.getTimestampOffset());
195+
expect(initialOffset).toBe(0);
196+
});
197+
198+
it("should update block offset", async () => {
199+
const [offset] = await shallResolve(manager.getTimestampOffset());
200+
expect(offset).toBe(0);
201+
202+
const blockOffset = 42;
203+
await shallPass(manager.setTimestampOffset(blockOffset));
204+
205+
const [newOffset] = await shallResolve(manager.getTimestampOffset());
206+
expect(newOffset).toBe(blockOffset);
207+
});
208+
});
209+
114210
describe("dev tests", () => {
115211
// Instantiate emulator and path to Cadence files
116212
beforeEach(async () => {

docs/api.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ import {
544544
emulator,
545545
executeScript,
546546
getBlockOffset,
547+
setBlockOffset,
547548
builtInMethods,
548549
sendTransaction,
549550
} from "flow-js-testing";
@@ -579,6 +580,102 @@ const main = async () => {
579580
main();
580581
```
581582

583+
### `getTimestampOffset()`
584+
585+
Returns current timestamp offset - amount of seconds added on top of real current timestamp.
586+
587+
#### Returns
588+
589+
| Type | Description |
590+
| ------ | ---------------------------------------------------------------------------- |
591+
| number | number representing amount of seconds added on top of real current timestamp |
592+
593+
#### Usage
594+
595+
```javascript
596+
import path from "path";
597+
import { init, emulator, getTimestampOffset } from "flow-js-testing";
598+
599+
const main = async () => {
600+
const basePath = path.resolve(__dirname, "../cadence");
601+
const port = 8080;
602+
603+
init(basePath, port);
604+
await emulator.start(port);
605+
606+
const [timestampOffset, err] = await getTimestampOffset();
607+
console.log({ timestampOffset }, { err });
608+
609+
await emulator.stop();
610+
};
611+
612+
main();
613+
```
614+
615+
> ⚠️ **Required:** In order for this method to work, you will need to pass code transformer to your interaction.
616+
> Framework exposes `builtInMethods` transformer to mock built in methods
617+
618+
### `setTimestampOffset(offset)`
619+
620+
Returns current timestamp offset - amount of seconds added on top of real current timestamp.
621+
622+
#### Arguments
623+
624+
| Name | Type | Description |
625+
| ---- | ---- | ----------- |
626+
627+
#### Returns
628+
629+
| Type | Description |
630+
| ------ | ---------------------------------------------------------------------------- |
631+
| number | number representing amount of seconds added on top of real current timestamp |
632+
633+
#### Usage
634+
635+
```javascript
636+
import path from "path";
637+
import {
638+
init,
639+
emulator,
640+
executeScript,
641+
getTimestampOffset,
642+
setTimestampOffset,
643+
builtInMethods,
644+
sendTransaction,
645+
} from "flow-js-testing";
646+
647+
const main = async () => {
648+
const basePath = path.resolve(__dirname, "../cadence");
649+
const port = 8080;
650+
651+
init(basePath, port);
652+
await emulator.start(port);
653+
654+
// Offset current timestamp by 10s
655+
await setTimestampOffset(10);
656+
657+
const [timestampOffset, err] = await getTimestampOffset();
658+
console.log({ timestampOffset }, { err });
659+
660+
// "getCurrentBlock().timestamp" in your Cadence code will be replaced by Manager to a mocked value
661+
const code = `
662+
pub fun main(): UInt64 {
663+
return getCurrentBlock().timestamp
664+
}
665+
`;
666+
667+
// "transformers" field expects array of functions to operate update the code.
668+
// We will pass single operator "builtInMethods" provided by the framework
669+
const transformers = [builtInMethods];
670+
const [result, error] = await executeScript({ code, transformers });
671+
console.log({ result }, { error });
672+
673+
await emulator.stop();
674+
};
675+
676+
main();
677+
```
678+
582679
## Jest Helpers
583680

584681
In order to simplify the process even further we've created several Jest-based methods, which will help you to catch

0 commit comments

Comments
 (0)