Skip to content

Commit

Permalink
Add PreUpgradeMethodArb
Browse files Browse the repository at this point in the history
  • Loading branch information
dansteren committed Dec 18, 2023
1 parent abbe181 commit 480d1e1
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 9 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ jobs:
"property_tests/tests/bool",
"property_tests/tests/canister_methods/init",
"property_tests/tests/canister_methods/post_upgrade",
"property_tests/tests/canister_methods/pre_upgrade",
"property_tests/tests/canister_methods/query",
"property_tests/tests/canister_methods/update",
"property_tests/tests/float32",
Expand Down
6 changes: 5 additions & 1 deletion property_tests/arbitraries/canister_arb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CorrespondingJSType } from './candid/corresponding_js_type';
import { InitMethod } from './canister_methods/init_method_arb';
import { PostUpgradeMethod } from './canister_methods/post_upgrade_arb';
import { UpdateMethod } from './canister_methods/update_method_arb';
import { PreUpgradeMethod } from './canister_methods/pre_upgrade_method_arb';

export type Canister = {
deployArgs: string[] | undefined;
Expand All @@ -19,7 +20,8 @@ export type CanisterMethod<
| QueryMethod
| UpdateMethod
| InitMethod<ParamAgentArgumentValue, ParamAgentResponseValue>
| PostUpgradeMethod<ParamAgentArgumentValue, ParamAgentResponseValue>;
| PostUpgradeMethod<ParamAgentArgumentValue, ParamAgentResponseValue>
| PreUpgradeMethod;

export type CanisterConfig<
ParamAgentArgumentValue extends CorrespondingJSType = undefined,
Expand All @@ -31,6 +33,7 @@ export type CanisterConfig<
ParamAgentArgumentValue,
ParamAgentResponseValue
>;
preUpgradeMethod?: PreUpgradeMethod;
queryMethods?: QueryMethod[];
updateMethods?: UpdateMethod[];
};
Expand All @@ -51,6 +54,7 @@ export function CanisterArb<
>[] = [
...(config.initMethod ? [config.initMethod] : []),
...(config.postUpgradeMethod ? [config.postUpgradeMethod] : []),
...(config.preUpgradeMethod ? [config.preUpgradeMethod] : []),
...(config.queryMethods ?? []),
...(config.updateMethods ?? [])
];
Expand Down
15 changes: 11 additions & 4 deletions property_tests/arbitraries/canister_methods/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import fc from 'fast-check';

import { CandidValueAndMeta } from '../candid/candid_value_and_meta_arb';
import { CandidReturnType } from '../candid/candid_return_type_arb';
import { CorrespondingJSType } from '../candid/corresponding_js_type';
import { Named } from '../..';
import { Test } from '../../../test';

export type BodyGenerator<
ParamAgentArgumentValue extends CorrespondingJSType,
ParamAgentResponseValue,
ParamAgentArgumentValue extends CorrespondingJSType = undefined,
ParamAgentResponseValue = undefined,
ReturnTypeAgentArgumentValue extends CorrespondingJSType = undefined,
ReturnTypeAgentResponseValue = undefined
> = (
Expand All @@ -20,8 +22,8 @@ export type BodyGenerator<
) => string;

export type TestsGenerator<
ParamAgentArgumentValue extends CorrespondingJSType,
ParamAgentResponseValue,
ParamAgentArgumentValue extends CorrespondingJSType = undefined,
ParamAgentResponseValue = undefined,
ReturnTypeAgentArgumentValue extends CorrespondingJSType = undefined,
ReturnTypeAgentResponseValue = undefined
> = (
Expand All @@ -37,6 +39,11 @@ export type TestsGenerator<

export type CallbackLocation = 'INLINE' | 'STANDALONE';

export const CallbackLocationArb = fc.constantFrom<CallbackLocation>(
'INLINE',
'STANDALONE'
);

export function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
TestsGenerator,
CallbackLocation,
isDefined,
generateCallback
generateCallback,
CallbackLocationArb
} from '.';
import { Test } from '../../../test';
import { VoidArb } from '../candid/primitive/void';
Expand Down Expand Up @@ -51,7 +52,7 @@ export function InitMethodArb<
UniqueIdentifierArb('canisterMethod'),
paramTypeArrayArb,
VoidArb(),
fc.constantFrom<CallbackLocation>('INLINE', 'STANDALONE'),
CallbackLocationArb,
UniqueIdentifierArb('typeDeclaration')
// TODO: This unique id would be better named globalScope or something
// But needs to match the same scope as typeDeclarations so I'm using
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import fc from 'fast-check';

import { UniqueIdentifierArb } from '../unique_identifier_arb';
import {
BodyGenerator,
TestsGenerator,
CallbackLocation,
generateCallback,
CallbackLocationArb
} from '.';
import { Test } from '../../../test';
import { VoidArb } from '../candid/primitive/void';

export type PreUpgradeMethod = {
imports: Set<string>;
globalDeclarations: string[];
sourceCode: string;
tests: Test[][];
};

export function PreUpgradeMethodArb(constraints: {
generateBody: BodyGenerator;
generateTests: TestsGenerator;
callbackLocation?: CallbackLocation;
}) {
return fc
.tuple(
UniqueIdentifierArb('canisterMethod'),
VoidArb(),
CallbackLocationArb,
UniqueIdentifierArb('typeDeclaration')
// TODO: This unique id would be better named globalScope or something
// But needs to match the same scope as typeDeclarations so I'm using
// that for now.
)
.map(
([
functionName,
returnType,
defaultCallbackLocation,
callbackName
]): PreUpgradeMethod => {
const callbackLocation =
constraints.callbackLocation ?? defaultCallbackLocation;

const imports = new Set(['preUpgrade']);

const callback = generateCallback(
[],
returnType,
constraints.generateBody,
callbackLocation,
callbackName
);

const globalDeclarations =
callbackLocation === 'STANDALONE' ? [callback] : [];

const sourceCode = `${functionName}: preUpgrade(${
callbackLocation === 'STANDALONE' ? callbackName : callback
})`;

const tests = constraints.generateTests(
functionName,
[],
returnType
);

return {
imports,
globalDeclarations,
sourceCode,
tests
};
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
TestsGenerator,
CallbackLocation,
isDefined,
generateCallback
generateCallback,
CallbackLocationArb
} from '.';
import { Test } from '../../../test';

Expand Down Expand Up @@ -57,7 +58,7 @@ export function QueryMethodArb<
UniqueIdentifierArb('canisterMethod'),
paramTypeArrayArb,
returnTypeArb,
fc.constantFrom<CallbackLocation>('INLINE', 'STANDALONE'),
CallbackLocationArb,
UniqueIdentifierArb('typeDeclaration')
// TODO: This unique id would be better named globalScope or something
// But needs to match the same scope as typeDeclarations so I'm using
Expand Down
116 changes: 116 additions & 0 deletions property_tests/tests/canister_methods/pre_upgrade/test/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import fc from 'fast-check';

import { deepEqual, getActor, runPropTests } from 'azle/property_tests';
import { CandidValueAndMetaArbWithoutFuncs as CandidValueAndMetaArb } from 'azle/property_tests/arbitraries/candid/candid_value_and_meta_arb';
import { CandidReturnTypeArb } from 'azle/property_tests/arbitraries/candid/candid_return_type_arb';
import { CorrespondingJSType } from 'azle/property_tests/arbitraries/candid/corresponding_js_type';
import {
CanisterArb,
CanisterConfig
} from 'azle/property_tests/arbitraries/canister_arb';
import { UpdateMethodArb } from 'azle/property_tests/arbitraries/canister_methods/update_method_arb';
import {
QueryMethod,
QueryMethodArb
} from 'azle/property_tests/arbitraries/canister_methods/query_method_arb';
import { PreUpgradeMethodArb } from 'azle/property_tests/arbitraries/canister_methods/pre_upgrade_method_arb';

const SimplePreUpgradeArb = PreUpgradeMethodArb({
generateBody: () =>
/*TS*/ `stable.insert(PRE_UPGRADE_HOOK_EXECUTED, true);`,
generateTests: () => []
});

const HeterogeneousQueryMethodArb = QueryMethodArb(
fc.array(CandidValueAndMetaArb()),
CandidReturnTypeArb(),
{
generateBody: (_, returnType) =>
`return ${returnType.src.valueLiteral}`,
generateTests: () => []
}
);

const HeterogeneousUpdateMethodArb = UpdateMethodArb(
fc.array(CandidValueAndMetaArb()),
CandidReturnTypeArb(),
{
generateBody: (_, returnType) =>
`return ${returnType.src.valueLiteral}`,
generateTests: () => []
}
);

const small = {
minLength: 0,
maxLength: 20
};

const CanisterConfigArb = fc
.tuple(
SimplePreUpgradeArb,
fc
.array(HeterogeneousQueryMethodArb, small)
.chain((queryMethods) =>
fc.constant([
generateGetPreUpgradeExecutedCanisterMethod(),
...queryMethods
])
),
fc.array(HeterogeneousUpdateMethodArb, small)
)

.map(
([preUpgradeMethod, queryMethods, updateMethods]): CanisterConfig<
CorrespondingJSType,
CorrespondingJSType
> => {
const globalDeclarations = [
/*TS*/ `const PRE_UPGRADE_HOOK_EXECUTED = 'PRE_UPGRADE_HOOK_EXECUTED';`,
/*TS*/ `let stable = StableBTreeMap<text, bool>(0);`
];

return {
globalDeclarations,
preUpgradeMethod,
queryMethods,
updateMethods
};
}
);

runPropTests(CanisterArb(CanisterConfigArb));

function generateGetPreUpgradeExecutedCanisterMethod(): QueryMethod {
return {
imports: new Set(['bool', 'query', 'StableBTreeMap']),
globalDeclarations: [],
sourceCode: /*TS*/ `getPreUpgradeExecuted: query([], bool,() => {
return stable.get(PRE_UPGRADE_HOOK_EXECUTED).Some === true
})`,
tests: [
[
{
name: `pre upgrade was not called after first deploy`,
test: async () => {
const actor = getActor(__dirname);
const result = await actor.getPreUpgradeExecuted();

return { Ok: deepEqual(result, false) };
}
}
],
[
{
name: `pre upgrade was called after second deploy`,
test: async () => {
const actor = getActor(__dirname);
const result = await actor.getPreUpgradeExecuted();

return { Ok: deepEqual(result, false) };
}
}
]
]
};
}

0 comments on commit 480d1e1

Please sign in to comment.