tex: update test's appearance+descrip.; test env
This commit is contained in:
parent
96d982b00e
commit
679714231b
@ -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}
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user