Chromia
Start Building

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:

src/test/news_feed_test.rell
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:

src/test/news_feed_test.rell
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:

src/test/news_feed_test.rell
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:

src/test/news_feed_test.rell
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.

Written for this site from Chromia’s public course. Code samples are the course’s own.