15 / 27Module 1: A Rell backend with FT accountsBeginner
Run unit tests
Once you have added input verification, running the tests produces failures with the message "User must sign this operation." This is the expected result: the original tests never signed their transactions, so the new checks reject them.
To make the tests pass, sign each test transaction with .sign(keypair). In test_create_entities, for example, Alice's keypair signs the transaction:
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);}The second test, test_follower_calculation, needs a signature on its transaction as well:
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(); ...}The last test involves two users. Alice signs the follow_user transaction, and Bob signs the make_post transactions. Add a constant for Bob's keypair and update the test as shown:
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(); // <-- } ...}With these changes, every test passes.
Additional testing
Add one more test to verify that a user cannot impersonate another user and that input validation behaves 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"));}You now know how to apply basic input validation to queries and operations. The next module covers more secure, structured account management with the ft-library.