Run unit tests
After making input verification changes to the queries, running the tests will result in failures with the message, "User must sign this operation." This indicates success, as the original test implementation did not account for signing.
To fix this, the test transactions need to be signed using .sign(keypair)
. For example, in the test_create_entities
function, the transaction is signed with Alice's keypair:
val alice_kp = rell.test.keypairs.alice; // <--
function test_create_entities() {
rell.test.tx()
.op(create_user("Alice", alice))
.op(create_user("Bob", bob))
.run();
assert_equals(user @ { } ( @sum 1 ), 2);
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(follow_user(bob))
.op(ft_auth_operation_for(alice))
.op(make_post("My post"))
.sign(alice_kp)
.run();
assert_true(is_following(alice, bob));
assert_equals(follower @ { } ( @sum 1 ), 1);
assert_equals(post @ { } ( @sum 1 ), 1);
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(unfollow_user(bob))
.sign(alice_kp)
.run();
assert_equals(follower @ { } ( @sum 1 ), 0);
}
In the second test case, test_follower_calculation
, the transaction also needs to be signed:
function test_follower_calculation() {
...
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(follow_user(bob))
.op(ft_auth_operation_for(alice))
.op(follow_user(charlie))
.sign(alice_kp) // <--
.run();
...
}
For the last test case, the follow_user
transaction should be signed by Alice, while the make_post
transactions should be signed by Bob. A new constant for Bob's keypair is added and the following adjustments are made:
val bob_kp = rell.test.keypairs.bob;
function test_pagination_of_posts() {
rell.test.tx()
.op(create_user("Alice", alice))
.op(create_user("Bob", bob)).run();
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(follow_user(bob))
.sign(alice_kp) // <--
.run();
for (i in range(5)) {
rell.test.tx().op(make_post(bob, "Content %d".format(i))).sign(bob_kp).run(); // <--
}
...
}
All tests now work as expected.
Additional testing
A new test case should be added to confirm that impersonation is not possible and that input validation works correctly:
val charlie_kp = rell.test.keypairs.charlie;
function test_input_verification() {
rell.test.tx()
.op(create_user("Alice", alice))
.op(create_user("Bob", bob)).run();
// Bob cannot impersonate alice
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(follow_user(bob))
.sign(bob_kp)
.run_must_fail();
rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(make_post("My malicous post"))
.sign(bob_kp)
.run_must_fail();
// Alice cannot follow non-existing charlie
val f1 = rell.test.tx()
.op(ft_auth_operation_for(alice))
.op(follow_user(charlie))
.sign(alice_kp)
.run_must_fail();
assert_true(f1.message.contains("does not exist"));
// Charlie cannot create post since he does not exist
val f2 = rell.test.tx()
.op(make_post("My secret post"))
.sign(charlie_kp)
.run_must_fail();
assert_true(f2.message.contains("Expected at least two operations"));
}
Congratulations! Basic input validation on queries and operations has been learned. In the next module, more secure and structured account management using the ft-library will be explored.