Урок 6 Тесты на FunC для смарт-контракта с op и query_id
Введение
В этом уроке мы напишем тесты для смарт-контракта созданного в пятом уроке в блокчейне TON и выполним их с помощью Blueprint.
Требования
Для прохождения данного урока вам достаточно установить Node.js. Желательно устанавливать одну из последних версий, например 18.
А также пройти пятый урок.
Задание пятого урока
Для удобства напомню здесь, что мы делали в пятом уроке. Задача смарт-контракта будет запоминать адрес, устанавливаемый менеджером и сообщать его всем, кто запросит, в частности следующая функциональность**:
- когда контракт получает сообщение от Менеджера с
op
равным 1
за которым следует какой-тоquery_id
, за которым следуетMsgAddress
, он должен сохранить полученный адрес в хранилище. - когда контракт получает внутреннее сообщение с любого адреса с
op
, равным 2, за которым следуетquery_id
, он должен ответить отправителю сообщением с телом, содержащим:op
равна 3- тот же
query_id
- Адрес менеджера
- Адрес, который был запомнен с момента последнего запроса менеджера (пустой адрес
addr_none
, если еще не было запроса менеджера) - Значение TON, прикрепленное к сообщению за вычетом платы за обработку.
- когда смарт-контракт получает любое другое сообщение, он должен выдать исключение.
Teсты для смарт-контракта с op и query_id
Для нашего прокси смарт-контракта мы напишем следующие тесты:
- Сохранение адресов с op = 1
- Адрес должен иметь возможность сохранять только менеджер
- Обработка op = 2
- Контракт должен выдавать исключение при любом другом опкоде
Прежде чем перейти к написанию тестов…
Откроем файл tests/AddressSaver.spec.ts
(название может отличаться, если вы назвали проект по-другому), в котором будут наши тесты. Вспомним из четвёртого урока, что нужно обновить значения в конфиге при деплое на те, которые мы там задавали. Также для удобства можно вынести объявление объекта deployer
за пределы функции beforeEach
чтобы иметь к нему доступ из всех тестов.
Тестируем op = 1
Генерируем случайный адрес функцией randomAddress()
, а затем вызываем метод sendChangeAddress
от имени кошелька deployer
.
В таком случае, должна присутствовать транзакция от deployer
к AddressSaver
с флагом success = true
(который означает, что выполнение всех фаз транзакции было успешно).
it('should change saved address by manager', async () => {
const address = randomAddress();
const result = await addressSaver.sendChangeAddress(
deployer.getSender(),
toNano('0.01'),
12345n,
address
);
expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: addressSaver.address,
success: true,
});
});
Тестируем исключение при вызове op = 1 не менеджером
В этом тесте делаем то же самое что и в предыдущем, но вызываем sendChangeAddress
от имени другого кошелька user
.
В таком случае флаг success
должен быть false
.
it('should not change saved address by anyone else', async () => {
let user = await blockchain.treasury('user');
const address = randomAddress();
const result = await addressSaver.sendChangeAddress(
user.getSender(),
toNano('0.01'),
12345n,
address
);
expect(result.transactions).toHaveTransaction({
from: user.address,
to: addressSaver.address,
success: false,
});
});
Тестируем op = 2
Вызываем sendChangeAddress
как и в первом тесте чтобы успешно поменять сохранённый адрес. Затем используя новый кошелёк user
вызываем sendRequestAddress
.
Такой вызов должен спровоцировать транзакцию от AddressSaver
к user
с телом сообщения, которое содержит в себе op = 3, query_id = 12345, deployer.address, address.
it('should return required data on `requestAddress` call', async () => {
const address = randomAddress();
await addressSaver.sendChangeAddress(
deployer.getSender(),
toNano('0.01'),
12345n,
address
);
let user = await blockchain.treasury('user');
const result = await addressSaver.sendRequestAddress(
user.getSender(),
toNano('0.01'),
12345n
);
expect(result.transactions).toHaveTransaction({
from: addressSaver.address,
to: user.address,
body: beginCell()
.storeUint(3, 32)
.storeUint(12345n, 64)
.storeAddress(deployer.address)
.storeAddress(address)
.endCell(),
});
});
Тестируем исключение при любых других опкодах
Для этого теста будем использовать метод send
у контракта Treasury
. Отправим, например, сообщение с op = 5.
Такая транзакция должна завершиться с exitCode = 3
, что мы и проверяем в тесте.
it('should throw on any other opcode', async () => {
const result = await deployer.send({
to: addressSaver.address,
value: toNano('0.01'),
body: beginCell().storeUint(5, 32).storeUint(12345n, 64).endCell(),
});
expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: addressSaver.address,
exitCode: 3,
});
});
Запустим тесты командой npx blueprint test
и мы должны увидеть следующее:
PASS tests/AddressSaver.spec.ts
AddressSaver
✓ should deploy (145 ms)
✓ should change saved address by manager (67 ms)
✓ should not change saved address by anyone else (67 ms)
✓ should return required data on `requestAddress` call (70 ms)
✓ should throw on any other opcode (89 ms)
Если какие то тесты у вас не прошли, просмотрите код и текст этого урока ещё раз. Также сверьте свой код смарт-контракта с кодом из предыдущего урока.
Заключение
Хотел сказать отдельное спасибо, тем кто донатит для поддержки проекта, это очень мотивирует и помогает выпускать уроки быстрее. Если вы хотите помочь проекту (быстрее выпускать уроки, перевести это все на английский итд), внизу на главной странице, есть адреса для донатов.