Skip to main content

Update tests

You need to understand how FT4 authentication works to get your tests working again. For each authenticated operation, you need to call auth.ft_auth or auth.evm_auth in the same transaction directly before the operation. Use the ft_auth_operation_for function from the ft library's test module:

src/test/my_news_feed_test.rell
import lib.ft4.test.utils.{ ft_auth_operation_for };

Inject the function calls in all transactions that make calls to make_post or follow_user. Remove the user_id argument from these operations. For example, in test_create_entities:

src/test/my_news_feed_test.rell
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)) // Auth operation
.op(follow_user(bob)) // Argument removed
.op(ft_auth_operation_for(alice)) // Auth operation
.op(make_post("My post")) // Argument removed
.sign(alice_kp)
.run();
assert_true(is_following(alice, bob));
assert_equals(follower @ { } (@sum 1), 1);
assert_equals(post @ { } (@sum 1), 1);
}

When changing the test_input_verification, we see how powerful this is. It becomes tough to impersonate someone else. In the last case, where Charlie tries to make a post, he cannot even send the transaction since he cannot create the auth-operation. The test now looks like:

src/test/my_news_feed_test.rell
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)) // <-- malicious auth operation
.op(follow_user(bob)) // <-- argument removed
.sign(bob_kp)
.run_must_fail();
rell.test.tx()
.op(ft_auth_operation_for(alice)) // <-- malicious auth operation
.op(make_post("My malicous post")) // <-- argument removed
.sign(bob_kp)
.run_must_fail();

// Alice cannot follow non-existing charlie
val f1 = rell.test.tx()
.op(ft_auth_operation_for(alice)) // <-- auth operation
.op(follow_user(charlie)) // <-- argument removed
.sign(alice_kp)
.run_must_fail();
assert_true(f1.message.contains("does not exist"));

// Charlie cannot create a post since he does not exist
val f2 = rell.test.tx()
//.op(ft_auth_operation_for(charlie)) // <-- Cannot create auth operation
.op(make_post("My secret post")) // <-- argument removed
.sign(charlie_kp)
.run_must_fail();
assert_true(f2.message.contains("Expected at least two operations"));
}

We also need to update test_follower_calculation and test_pagination_of_posts:

src/test/my_news_feed_test.rell
function test_follower_calculation() {
rell.test.tx()
.op(create_user("Alice", alice))
.op(create_user("Bob", bob))
.op(create_user("Charlie", charlie))
.run();

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();

assert_true(is_following(alice, bob));
assert_true(is_following(alice, charlie));
assert_equals(get_following_count(alice), 2);
assert_equals(get_following_count(bob), 0);

assert_equals(get_followers_count(alice), 0);
assert_equals(get_followers_count(bob), 1);
}

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(ft_auth_operation_for(bob))
.op(make_post("Content %d".format(i)))
.sign(bob_kp)
.run();
}

val initial_posts = get_posts(alice, 0, 4);
assert_equals(initial_posts.pointer, 4);
assert_equals(initial_posts.posts.size(), 4);
val last_posts = get_posts(alice, initial_posts.pointer, 4);
assert_equals(last_posts.pointer, 5);
assert_equals(last_posts.posts.size(), 1);
}

In the next lesson, we'll break down the app into several modules to create a better-structured app.