onflow/cadence-tools

[Test] Unable to use `Test.moveTime`

Opened this issue ยท 4 comments

Current Behavior

Using Test.moveTime doesn't seem to impact the timestamp of a block

Expected Behavior

Test.moveTime should result in getCurrentBlock().timestamp changing

Steps To Reproduce

pub fun testMoveTime() {
    let before = getCurrentBlock().timestamp
    Test.moveTime(by: 1.0)
    let after = getCurrentBlock().timestamp
    assert(after - before >= 1.0) // this always fails
}

Also fails if you use scripts to obtain the time instead of using the testing framework directly:

pub fun getCurrentTime(): UFix64 {
    return scriptExecutor("util/get_current_time.cdc", [])! as! UFix64
}
pub fun testMoveTime() {
    let before = getCurrentTime()
    Test.moveTime(by: 1.0)
    heartbeat()
    let after = getCurrentTime()

    assert(after - before == 1.0)
}

Script:

pub fun main(): UFix64 {
    return getCurrentBlock().timestamp
}

Environment

- Cadence version: v1.9.2
- Network: Testing Framework

Did some further debugging. Seems like this actually works when you grab time directly from the Blockchain itself, you cannot use the testing framework's time. So anyone who uses Test.moveTime needs to ensure they're grabbing time with a helper script like what I had to do.

It seems like the use of log threw some of this off when running directly in the vscode extension, once I removed all logs, things passed as expected so long as I only grabbed timestamps using a script of some kind

#265

Final piece I've found is that Test.moveTime seems to only work with whole numbers, but it accepts a UFix64


pub fun testMoveTime() {
    let before = getCurrentTime()
    Test.moveTime(by: 1.5)
    let after = getCurrentTime()

    log(before)
    log(after)
    assert(after - before >= 1.5)
}

Logs out:

9:33AM INF LOG: 1703698412.00000000
9:33AM INF LOG: 1703698413.00000000

Thanks for the report @austinkline ๐Ÿ™‡ ,

You are in fact correct in both of your findings. Test.moveTime only affects the Blockchain's time, and the reason behind it is that contracts under testing are deployed on the Blockchain, so they would fetch the current block data from the Blockchain, e.g.:

access(all) contract TimeLocker {
    access(all) let lockPeriod: UFix64
    access(all) let lockedAt: UFix64

    init(lockedAt: UFix64) {
        self.lockedAt = lockedAt
        // Lock period is 30 days, in the form of seconds.
        self.lockPeriod = UFix64(30 * 24 * 60 * 60)
    }

    access(all)
    fun isOpen(): Bool {
        let currentTime = getCurrentBlock().timestamp

        return currentTime > (self.lockedAt + self.lockPeriod)
    }
}

In the above sample contract, isOpen() would check the timestamp from the Blockchain's current block.
The stdlib functions in the script environment that runs test scripts, are not wired to that Blockchain, that's why you would get different results. I am not sure if it is possible to achieve that functionality. I will check it out though.

The reason for accepting UFix64 as the delta. in Test.moveTime is because the timestamp field from getCurrentBlock() is also of UFix64 type. However, we can only advance the system clock in whole seconds. I am not sure if we can have smaller granularity than seconds, I can understand your confusion though ๐Ÿ˜…

I hope this clarifies things better ๐Ÿ™

EDIT: We cannot have granularity smaller than seconds, neither in testnet/mainnet.

The clock should probably be changed to support sub-second granularity.