1
0

tex: update test's appearance+descrip.; test env

This commit is contained in:
surtur 2023-08-18 17:51:13 +02:00
parent 96d982b00e
commit 679714231b
Signed by: wanderer
SSH Key Fingerprint: SHA256:MdCZyJ2sHLltrLBp0xQO0O1qTW9BT/xl5nXkDvhlMCI

@ -1105,51 +1105,58 @@ then after pushing to remote in the CI.
\n{3}{func TestUserExists(t *testing.T)}
An example integration test shown in Listing~\ref{integrationtest} can be seen
to declare a helper function \texttt{getCtx() context.Context}, which takes no
arguments and returns a new\\ \texttt{context.Context} initialised with a value
of the global logger, which is how the logger gets injected into the user
module functions. The function \texttt{TestUserExists(t *testing.T)} first
declares a database connection string and attempting to open a connection to
the database. The database in use here is SQLite3 running in memory mode,
meaning no file is actually written to disk during this process. Since the
testing data is not needed after the test, this is deemed good enough. Next, a
defer statement calling the \texttt{Close()} method on the database object is
made, which is the idiomatic Go way of closing files and network connections
(which are also an abstraction over files on UNIX-like operating systems such
as GNU/Linux). The \emph{defer} statement gets called after all of the
at line 10 to declare a helper function \texttt{getCtx() context.Context},
which takes no arguments and returns a new\\ \texttt{context.Context}
initialised with the value of the global logger, which is how the logger gets
injected into the user module functions. The actual test function with the
signature \texttt{TestUserExists(t *testing.T)} defines a database connection
string at line 21 and attempts to open a connection to the database. The
database in use here is SQLite3 running in memory mode, meaning no file is
actually written to disk during this process. Since the testing data is not
needed after the test, this is desirable. Next, a defer statement calls the
\texttt{Close()} method on the database object , which is the Go idiomatic way
of closing files and network connections (which are also an abstraction over
files on UNIX-like operating systems such as GNU/Linux). Contrary to where it
is declared, the \emph{defer} statement is onlycalled after all of the
statements in the surrounding function, which makes sure no file descriptors
(FDs) are leaked and the file is properly closed when the function returns.
In the next step a database schema creation is attempted, handling the
potential error in a Go idiomatic way, which uses the return value from the
In the next step at line 25 a database schema creation is attempted, handling
the potential error in a Go idiomatic way, which uses the return value from the
function in an assignment to a variable declared in the \texttt{if} statement,
and checks whether the \texttt{err} was \texttt{nil} or not. In case the
\texttt{err} was not \texttt{nil}, i.e.\ \emph{there was an error in the callee
function}, the condition evaluates to \texttt{true}, which is followed by
entering the inner block. Inside the inner block, the error is announced to the
user (likely a developer running the test in this case) and the testing
object's \texttt{FailNow()} method is called, which marks the test function as
having failed, and thus stops its execution. In this case, that is the desired
entering the inner block. Inside it, the error is announced to the user (likely
a developer running the test in this case) and the testing object's
\texttt{FailNow()} method is called. That marks the test function as having
failed, and thus stops its execution. In this case, that is the desired
outcome, since if the database schema creation call fails, there really is no
point in continuing testing of user creation.
Conversely, if the schema does get created without an error, the code continues
to declare a few variables: \texttt{username}, \texttt{email} and \texttt{ctx},
where the context injected with the logger is saved. Some of them are
subsequently passed into the \texttt{UsernameExists} function, \texttt{ctx} as
the first argument, with the database pointer and username being passed next,
while the \texttt{email} variable is only used at a later stage, but was
declared here to give a sense of grouping. The error value returned from this
function is again checked and if everything goes well, the value of the
\texttt{usernameFound} boolean is checked next.
point in continuing the testing of user creation.
\\
Conversely, if the schema \emph{does} get created without an error, the code
continues to declare a few variables (lines 30-32): \texttt{username},
\texttt{email} and \texttt{ctx}, where the context injected with the logger is
saved. Two of them are subsequently (line 33) passed into the
\texttt{UsernameExists} function, \texttt{ctx} being the first argument and the
database pointer and \texttt{username} following, while the \texttt{email}
variable is only used at a later stage (line 46). The point of declaring them
together is to give a sense of relatedness. The error value returned from this
function is again checked (line 33) and if everything goes well, the
\texttt{usernameFound} boolean value is checked next at line 38.
\smallskip
\smallskip
\begin{lstlisting}[language=Go, caption={Example integration test.},
label=integrationtest,basicstyle=\linespread{0.9}\scriptsize\ttfamily,
% numbers=left, numberstyle=\tiny\ttfamily\color{codegray},
backgroundcolor=\color{lstbg},
morekeywords=any
numbers=left,
numberstyle=\linespread{0.9}\scriptsize\ttfamily,
frame=l,
framesep=18.5pt,
framerule=0.1pt,
xleftmargin=18.7pt,
otherkeywords={\%s, \%q, \%v},
]
// modules/user/user_test.go
package user
@ -1166,7 +1173,6 @@ import (
func getCtx() context.Context {
l := slogging.Init(false)
ctx := context.WithValue(context.Background(), CtxKey{}, l)
return ctx
}
@ -1186,22 +1192,15 @@ func TestUserExists(t *testing.T) {
usernameFound, err := UsernameExists(ctx, db, username)
if err != nil {
t.Errorf("error checking for username {%s} existence: %q",
username,
err,
)
t.Errorf("error checking for username {%s} existence: %q", username, err)
}
if usernameFound {
t.Errorf("unexpected: user{%s} should not have been found",
username,
)
t.Errorf("unexpected: user{%s} should not have been found", username)
}
if _, err := EmailExists(ctx, db, email); err != nil {
t.Errorf("unexpected: user email '%s' should not have been found",
email,
)
t.Errorf("unexpected: user email '%s' should not have been found", email)
}
usr, err := CreateUser(ctx, db, email, username, "so strong")
@ -1222,16 +1221,23 @@ func TestUserExists(t *testing.T) {
\end{lstlisting}
Since the database has just been created, there should be no users, which is
checked in the next \texttt{if} statement. The same check is then performed for
the earlier-declared user email that is also expected to fail.
checked in the body of the \texttt{if} statement (line 35). The same check is
then performed using an email (line 42), which is also correctly expected to
fail.
The final statements of the described test attempts a user creation call, which
is again checked for both error and \emph{nilability}. The test continues with
more similar checks but it has been cut short for brevity.
The final statements of the described test attempts to create a user by calling
the function \texttt{CreateUser(...)} at line 46, whose return values are again
checked for both error and \emph{nilability}, respectively. The test continues
with more of the checks similar to what has been described so far, but the rest
was omitted for brevity.
A neat thing about error handling in Go is that it allows for very easy
checking of all paths, not just the \emph{happy path} where there are no
issues.
As was just demonstrated in the test, a neat thing about error handling in Go
is that it allows for very easy checking of all code paths, not just the
\emph{happy path} where there are no issues. The recommended approach of
immediately explicitly handling (or deciding to ignore) the error is in
author's view superior to wrapping hundreds of lines in \texttt{try} blocks and
then \emph{catching} (or not) \emph{all the} exceptions, as is the practice in
some other languages.
\n{2}{Test environment}
@ -1242,8 +1248,8 @@ by \emph{Let's Encrypt}\allowbreak issued, short-lived, ECDSA
\texttt{secp384r1} curve TLS certificate, and configured with strict CSP. It is
a test instance; therefore limits (and rate-limits) to prevent abuse might be
imposed.
The application in the test environment is available over both modern IPv6 and
\\
The test environment makes the program available over both modern IPv6 and
legacy IPv4 protocols, to maximise accessibility. Redirects have been set up
from plain HTTP to HTTPS, as well as from \texttt{www} to non-\texttt{www}
domain. The subject domain configuration has been hardened with a \texttt{CAA}
@ -1256,17 +1262,19 @@ speaking HTTP only connect to it (and the subdomains) using TLS.
The whole deployment has been orchestrated using an Ansible\footnotemark{}
playbook created for this occasion, with the aim to have the whole deployment
process reliably automated. At the same time, it is now described reasonably
well in the code. An effort has been made to make the playbook idempotent. Its
code is available at \url{https://git.dotya.ml/mirre-mt/ansible-pcmt.git}.
process reliably automated, focusing on idempotence. At the same time, it is
now described reasonably well in the code. Its code is available at
\url{https://git.dotya.ml/mirre-mt/ansible-pcmt.git}.
\footnotetext{A Nix-ops approach was also considered, however, Ansible was
picked as more suitable since the existing host runs Arch.}
deemed more suitable since the existing host runs Arch.}
\n{3}{Deployment validation}
TODO: show the results of testing the app in prod using: \url{https://testssl.sh/}.
% TODO: show the results of testing the app in prod using:
% \url{https://testssl.sh/} and
% \url{https://gtmetrix.com/reports/testpcmt.dotya.ml/}.
The deployed application has been validated using the \textit{Security Headers}
tool (see \url{https://securityheaders.com/?q=https%3A%2F%2Ftestpcmt.dotya.ml}),
@ -1319,7 +1327,7 @@ HSTS is deployed (including for the subdomains), the server supports TLS 1.3,
the DNS Certificate Authority Authorisation (CAA) has been configured for the
domain, with the overall grade being A+.
\obr{Quallys SSL Labs}{fig:ssllabs}{.70}{graphics/screen-sslLabs}
\obr{Quallys SSL Labs}{fig:ssllabs}{.79}{graphics/screen-sslLabs}