Skip to main content

Run unit tests

After making the input verification changes to the queries, running the tests will show that they fail with the message User must sign this operation. This is a good sign because the original test implementation didn't consider signing.

We need to add .sign(keypair) to the test transactions to fix this. For example, in test_create_entities, we sign the transaction with Alice's keypair:

src/test/my_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(follow_user(alice, bob))
.op(make_post(alice, "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(unfollow_user(alice, bob))
.sign(alice_kp) // <--
.run();
assert_false(is_following(alice, bob));
assert_equals(follower @ { } (@sum 1), 0);
}

Similarly, for the second test case test_follower_calculation:

src/test/my_news_feed_test.rell
function test_follower_calculation() {
...
rell.test.tx()
.op(follow_user(alice, bob))
.op(follow_user(alice, charlie))
.sign(alice_kp) // <--
.run();
...
}

For the last test case, we need Alice to sign the follow_user transaction and Bob to sign the make_post transactions. We add another constant for Bob's keypair and make the following adjustment:

src/test/my_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(follow_user(alice, 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(); // <--
}
...
}

Now, all the tests are working again.

Additional testing

Let's add a new test case to ensure that impersonation can't happen and that the input validation works:

src/test/my_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(follow_user(alice, bob))
.sign(bob_kp)
.run_must_fail();
rell.test.tx()
.op(make_post(alice, "My malicious post"))
.sign(bob_kp)
.run_must_fail();

// Alice cannot follow non-existing Charlie
val f1 = rell.test.tx()
.op(follow_user(alice, charlie))
.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(make_post(charlie, "My secret post"))
.sign(charlie_kp)
.run_must_fail();
assert_true(f2.message.contains("does not exist"));
}

Manual testing

To test this manually, like in the previous module, we must store the generated keypairs, not just the public keys. We can do this by

chr keygen --file <target>

Chromia CLI looks for .chromia/config, but it's possible to override this using the --secret flag of chr tx. We can save the keypairs for Alice and Bob as follows:

chr keygen --file .chromia/config-alice
chr keygen --file .chromia/config-bob

You can use these keypairs when making transactions, ensuring you sign with the correct key.

chr node start
chr tx --await create_user Alice 'x"<alice-pubkey>"'
chr tx --await create_user Bob 'x"<bob-pubkey>"'
chr tx --await --secret .chromia/config-bob follow_user 'x"<alice-pubkey>"' 'x"<bob-pubkey>"' # Fails
chr tx --await --secret .chromia/config-alice follow_user 'x"<alice-pubkey>"' 'x"<bob-pubkey>"' # Succeeds

Congratulations! You've learned how to perform basic input validation on queries and operations. In the next module, we'll explore how to manage accounts more structured and securely using the ft-library.