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:
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
:
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:
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
:
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.