We were converting markdown to HTML every time we saved a record, which
has the same problems as sanitizing HTML before saving it to the
database, particularly because the body of a legislation draft is stored
in a translations table.
Performance-wise this isn't a problem: converting a text with more than
200_000 characters takes about a milisecond on my machine.
Note we need to modify a migration generated by globalize, since the
method `create_translation_table!` would fail now that we don't define
`translates :body_html` in the model.
Sanitizing descriptions before saving a record has a few drawbacks:
1. It makes the application rely on data being safe in the database. If
somehow dangerous data enters the database, the application will be
vulnerable to XSS attacks
2. It makes the code complicated
3. It isn't backwards compatible; if we decide to disallow a certain
HTML tag in the future, we'd need to sanitize existing data.
On the other hand, sanitizing the data in the view means we don't need
to triple-check dangerous HTML has already been stripped when we see the
method `auto_link_already_sanitized_html`, since now every time we use
it we sanitize the text in the same line we call this method.
We could also sanitize the data twice, both when saving to the database
and when displaying values in the view. However, doing so wouldn't make
the application safer, since we sanitize text introduced through
textarea fields but we don't sanitize text introduced through input
fields.
Finally, we could also overwrite the `description` method so it
sanitizes the text. But we're already introducing Globalize which
overwrites that method, and overwriting it again is a bit too confusing
in my humble opinion. It can also lead to hard-to-debug behaviour.
There were some confusing definitions regarding the valuation of budget
investments.
In the controller, `CommentableActions` was included, which includes the
update action.
In the abilities, a valuator was given permission to update an
investment.
However, the action to update an investment didn't work because there is
no route defined to do so.
The ability was defined so valuators could access the "edit" action,
which will not call the "update" action but the "valuate" action. Since
internally "edit" and "update" use the same permission, it worked.
But then we added permission for regular users to update budget
investments, and these permissions were allowing valuators to update
another user's investment.
After this change, everything seems to work properly since we check
authorization in the controller itself instead of using abilities.
Using the `_html` suffix automatically marks texts as HTML safe, so
doing so on sanitized texts is redundant.
Note flash texts are not sanitized the moment they are generated, but
are sanitized when displayed in the view.
We use this method in two different scenarios. In an AJAX request, we
don't want to return every booth if the search is blank. However, in a
normal HTTP GET request, we want to return every record when the search
is empty, as we do everywhere else.
It's possible the behaviour of the AJAX call is unusual, since it
searches all booths, and not just the ones assigned to a poll. If we
changed this behaviour, we could simplify the code and remove the
`quick_search` method.
We can change the code a bit so the useless assignment is either part of
the setup (where only another variable was present) or isolated in the
"action" part of the test.
These variables can be considered a block, and so removing them doesn't
make the test much harder to undestand.
Sometimes these variables formed the setup, sometimes they formed an
isolated part of the setup, and sometimes they were the part of the test
that made the test different from other tests.
The limit parameter wasn't specified in the test but in the default
value in the database, making the test hard to read.
Since now we've moved the other processes to separate tests, now we can
create four processes using `times` and keep the test simple.
In the scenario where we want to test scopes and use `match_array`, we
usually declare variables we never use, which raises a warning in the
Ruby interpreter (since the main cause for an unused variable is a
typo).
So I've decided to just split the tests into cases where every record is
returned and cases were no records are returned, just like we do in
other places.
There are several other options we've considered:
1. Don't declare unused variables, but declare the ones we use
2. Prefix unused variables with un underscore
3. Declare just one variable being an array containing all elements, and
access the elements using Array#[]
4. Don't declare any variables, and compare results against attributes
such as titles
None of these options was met with enthusiasm.
Assigning a variable to each budget we declare results in useless
assignments. We could just delete the three useless assignments and
leave the fourth one, but I find the code easier to read if we use the
name of the budgets to differenciate between them. This way we also keep
the code vertically aligned.
Unless we're using the booth assignment for something else in the test,
when creating a voter from booth, specifying the poll and the booth is
enough to create a booth assignment.
We were only using it in a few places.
I've left the current `final: true` statement in a few places where
using a trait would break vertical alignment, just in case.
Note we usually cannot make it simple because officer assignments are
usually assigned to both a poll and a booth, and on a certain date.
However, in the few cases where the booth nor the date don't matter, we
can make the code a bit easier to read.
While in theory we wouldn't need to use the `transient` nor the
`after(:create)` because there's already a `has_many :through`
association with followers, Factory Bot / ActiveRecord don't
automatically associate the followable, resulting in an invalid record
exception.
When we already have tests checking which records are included, in the
tests testing records are not included we can just generate records
which will not be included and test against an empty array.
We were already using this approach in some tests. This way we also
avoid useless assignments.
We're using `eq` and `match_array` in most places, but there were a few
places where we were still checking each element is included in the
array. This is a bit dangerous, because the array could have duplicate
elements, and we wouldn't detect them with `include`.
The test or the draft phase legislation filter was using an entirely
different set of records, but was still creating the records used in the
open and past filter as well.
This made it hard to test the filter, since it returned records from
both sets.
Grouping the past and open filters together guarantees their records
won't be created in the phase draft test, and so we can test the exact
contents of the array.
We were using `to_sentence` to check the order, while it's easier to
just check the exact contents of the array.
Furthermore, using `to_sentence` checked the order of the array, so it
was a potentially flaky spec since the method doesn't specify an order
and PostgreSQL doesn't guarantee the order of the records.
The group is automatically assigned when we assign the heading. The
budget isn't needed either, except for a special case related to the
reason to be rejected.
These tests are only checking which proposals are not included in the
recommendations, so we don't need to sort the included ones, just like
we don't use the cached votes up attribute in the tests preceeding these
ones.