diff --git a/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts b/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts index 1ea382b08a..36fcc42927 100644 --- a/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts +++ b/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts @@ -1,5 +1,8 @@ import { acceptMessage, + call, + heartbeat, + id, IDL, init, inReplicatedExecution, @@ -8,6 +11,7 @@ import { postUpgrade, preUpgrade, query, + setTimer, StableBTreeMap, update } from 'azle'; @@ -20,7 +24,8 @@ export default class { 'PRE_UPGRADE_VERSION', boolean >(0); - inspectMessageIsInReplicatedExecution: boolean | null = null; + timerIsInReplicatedExecution: boolean | null = null; + heartbeatIsInReplicatedExecution: boolean | null = null; @init init(): void { @@ -39,6 +44,9 @@ export default class { @postUpgrade postUpgrade(): void { this.postUpgradeIsInReplicatedExecution = inReplicatedExecution(); + setTimer(0n, () => { + this.timerIsInReplicatedExecution = inReplicatedExecution(); + }); } @query([], IDL.Opt(IDL.Bool)) @@ -70,39 +78,66 @@ export default class { } } - @inspectMessage - inspectMessage(): void { - if (methodName() === 'getInspectMessageIsInReplicatedExecution') { - if ( - inReplicatedExecution() === - this.inspectMessageIsInReplicatedExecution - ) { - acceptMessage(); - } - } else { - acceptMessage(); - } + @heartbeat + heartbeat(): void { + this.heartbeatIsInReplicatedExecution = inReplicatedExecution(); } - @update - setInspectMessageIsInReplicatedExecution(): void { - this.inspectMessageIsInReplicatedExecution = inReplicatedExecution(); + @query([], IDL.Opt(IDL.Bool)) + getHeartbeatIsInReplicatedExecution(): [boolean] | [] { + if (this.heartbeatIsInReplicatedExecution === null) { + return []; + } else { + return [this.heartbeatIsInReplicatedExecution]; + } } @query([], IDL.Opt(IDL.Bool)) - getInspectMessageIsInReplicatedExecution(): [boolean] | [] { - if (this.inspectMessageIsInReplicatedExecution === null) { + getTimerIsInReplicatedExecution(): [boolean] | [] { + if (this.timerIsInReplicatedExecution === null) { return []; } else { - return [this.inspectMessageIsInReplicatedExecution]; + return [this.timerIsInReplicatedExecution]; } } + @inspectMessage + inspectMessage(): void { + if ( + methodName() === + 'getInspectMessageIsInReplicatedExecutionWhenInspectingUpdates' + ) { + if (inReplicatedExecution() === true) { + acceptMessage(); + } + return; + } + + acceptMessage(); + } + + @update([], IDL.Bool) + getInspectMessageIsInReplicatedExecution(): boolean { + return true; + } + @query([], IDL.Bool) getQueryIsInReplicatedExecution(): boolean { return inReplicatedExecution(); } + @update([], IDL.Bool) + async getQueryInReplicatedModeIsInReplicatedExecution(): Promise { + return await call(id(), 'getQueryIsInReplicatedExecution', { + returnIdlType: IDL.Bool + }); + } + + @query([], IDL.Bool, { composite: true }) + getCompositeQueryIsInReplicatedExecution(): boolean { + return inReplicatedExecution(); + } + @update([], IDL.Bool) getUpdateIsInReplicatedExecution(): boolean { return inReplicatedExecution(); diff --git a/examples/stable/test/property/ic_api/in_replicated_execution/test/tests.ts b/examples/stable/test/property/ic_api/in_replicated_execution/test/tests.ts index a81c130623..b1280da8ea 100644 --- a/examples/stable/test/property/ic_api/in_replicated_execution/test/tests.ts +++ b/examples/stable/test/property/ic_api/in_replicated_execution/test/tests.ts @@ -5,20 +5,88 @@ import { _SERVICE as Actor } from './dfx_generated/canister/canister.did'; export function getTests(): Test { return () => { - it('gets the canister version from the canister', async () => { + it('verifies init is running in replicated execution', async () => { const actor = await getCanisterActor('canister'); await checkInitIsInReplicatedExecution(actor, true); await checkUpgradeIsInReplicatedExecution(actor, true); - await checkInspectIsInReplicatedExecution(actor); - await checkQueryAndUpdateIsInReplicatedExecution(actor); + }); + + it('verifies inspectMessage is running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + for (let i = 0; i < 10; i++) { + expect( + await actor.getInspectMessageIsInReplicatedExecution() + ).toBe(true); + } + }); + + it('verifies non-replicated queries are not running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + for (let i = 0; i < 10; i++) { + expect(await actor.getQueryIsInReplicatedExecution()).toBe( + false + ); + } + }); + + it('verifies replicated queries are running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + for (let i = 0; i < 10; i++) { + expect( + await actor.getQueryInReplicatedModeIsInReplicatedExecution() + ).toBe(true); + } + }); + + it('verifies composite queries are not running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + for (let i = 0; i < 10; i++) { + expect( + await actor.getCompositeQueryIsInReplicatedExecution() + ).toBe(false); + } + }); + + it('verifies update is running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + for (let i = 0; i < 10; i++) { + expect(await actor.getUpdateIsInReplicatedExecution()).toBe( + true + ); + } + }); + + it('verifies heartbeat is running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + expect( + await actor.getHeartbeatIsInReplicatedExecution() + ).toStrictEqual([true]); + }); + + it('redeploys the canister', async () => { execSync(`dfx deploy canister --upgrade-unchanged`); + }); + + it('verifies pre and post upgrade are running in replicated execution after redeploy', async () => { + const actor = await getCanisterActor('canister'); await checkInitIsInReplicatedExecution(actor, false); await checkUpgradeIsInReplicatedExecution(actor, false); - await checkInspectIsInReplicatedExecution(actor); - await checkQueryAndUpdateIsInReplicatedExecution(actor); + }); + + it('verifies timer is running in replicated execution', async () => { + const actor = await getCanisterActor('canister'); + + expect(await actor.getTimerIsInReplicatedExecution()).toStrictEqual( + [true] + ); }); it('asserts inReplicatedExecution static and runtime types', async () => { @@ -64,20 +132,3 @@ async function checkUpgradeIsInReplicatedExecution( expect(preUpgradeCanisterVersionAfterUpgrade[0]).toBe(true); } } - -async function checkQueryAndUpdateIsInReplicatedExecution( - actor: Actor -): Promise { - expect(await actor.getQueryIsInReplicatedExecution()).toBe(false); - expect(await actor.getUpdateIsInReplicatedExecution()).toBe(true); -} - -async function checkInspectIsInReplicatedExecution( - actor: Actor -): Promise { - await actor.setInspectMessageIsInReplicatedExecution(); - const inspectMessageIsInReplicatedExecution = - await actor.getInspectMessageIsInReplicatedExecution(); - - expect(inspectMessageIsInReplicatedExecution[0]).toBe(true); // TODO: Is this because it's in front of an update? -}