github.com/crowdsecurity/crowdsec@v1.6.1/test/README.md (about)

     1  
     2  # What is this?
     3  
     4  This directory contains scripts for functional testing. The tests are run with
     5  the [bats-core](https://github.com/bats-core/bats-core) framework, which is an
     6  active fork of the older BATS (Bash Automated Testing System).
     7  
     8  With the addition of [the ansible playbooks](ansible/README.md) it is possible
     9  to use VMs to test the binary packages, service management and other CPU
    10  architectures.
    11  
    12  ### cscli
    13  
    14  | Feature               | Covered            | Notes                      |
    15  | :-------------------- | :----------------- | :------------------------- |
    16  | `cscli alerts`        | -                  |                            |
    17  | `cscli bouncers`      | `10_bouncers`      |                            |
    18  | `cscli capi`          | `01_base`          | `status` only              |
    19  | `cscli collections`   | `20_collections`   |                            |
    20  | `cscli config`        | `01_base`          | minimal testing (no crash) |
    21  | `cscli dashboard`     | -                  | docker inside docker 😞    |
    22  | `cscli decisions`     | `9[78]_ipv[46]*`   |                            |
    23  | `cscli hub`           | `dyn_bats/99_hub`  |                            |
    24  | `cscli lapi`          | `01_base`          |                            |
    25  | `cscli machines`      | `30_machines`      |                            |
    26  | `cscli metrics`       | -                  |                            |
    27  | `cscli parsers`       | -                  |                            |
    28  | `cscli postoverflows` | -                  |                            |
    29  | `cscli scenarios`     | -                  |                            |
    30  | `cscli simulation`    | `50_simulation`    |                            |
    31  | `cscli version`       | `01_base`          |                            |
    32  
    33  ### crowdsec
    34  
    35  | Feature                        | Covered        | Notes                                      |
    36  | :----------------------------- | :------------- | :----------------------------------------- |
    37  | `systemctl` start/stop/restart | -              |                                            |
    38  | agent behavior                 | `40_live-ban`  | minimal testing  (simple ssh-bf detection) |
    39  | forensic mode                  | `40_cold-logs` | minimal testing (simple ssh-bf detection)  |
    40  | starting without LAPI          | `02_nolapi`    |                                            |
    41  | starting without agent         | `03_noagent`   |                                            |
    42  | starting without CAPI          | `04_nocapi`    |                                            |
    43  | prometheus testing             | -              |                                            |
    44  
    45  ### API
    46  
    47  | Feature            | Covered          | Notes        |
    48  | :----------------- | :--------------- | :----------- |
    49  | alerts GET/POST    | `9[78]_ipv[46]*` |              |
    50  | decisions GET/POST | `9[78]_ipv[46]*` |              |
    51  | stream mode        | `99_lapi-stream-mode |          |
    52  
    53  
    54  # How to use it
    55  
    56  ## pre-requisites
    57  
    58   - `git submodule init; git submodule update`
    59   - `base64`
    60   - `bash>=4.4`
    61   - `curl`
    62   - `daemonize`
    63   - `jq`
    64   - `nc`
    65   - `openssl`
    66   - `python3`
    67  
    68  ## Running all tests
    69  
    70  Run `make clean bats-all` to perform a test build + run.
    71  To repeat test runs without rebuilding crowdsec, use `make bats-test`.
    72  
    73  
    74  ## Debugging tests
    75  
    76  See `./test/run-tests --help` to run/debug specific tests.
    77  
    78  Example: `./test/run-tests test/bats/02_nolapi.bats -f "cscli config backup"` (the string is a regexp).
    79  You need to provide a path for a test file or directory (even if it's the full 'test/bats') to use the `-f` option.
    80  
    81  
    82  # How does it work?
    83  
    84  In BATS, you write tests in the form of Bash functions that have unique
    85  descriptions (the name of the test). You can do most things that you can
    86  normally do in a shell function. If there is any error condition, the test
    87  fails. A set of functions is provided to implement assertions, and a mechanism
    88  of `setup`/`teardown` is provided at the level of individual tests (functions)
    89  or group of tests (files).
    90  
    91  The stdout/stderr of the commands within the test function are captured by
    92  bats-core and will only be shown if the test fails. If you want to always print
    93  something to debug your test case, you can redirect the output to the file
    94  descriptor 3:
    95  
    96  ```sh
    97  @test "mytest" {
    98     echo "hello world!" >&3
    99     run some-command
   100     assert_success
   101     echo "goodbye." >&3
   102  }
   103  ```
   104  
   105  If you do that, please remove it once the test development is finished, because
   106  this practice breaks the TAP protocol (unless each line has a '#' as first
   107  character, but really, it's better to avoid unnecessary output when tests succeed).
   108  
   109  You can find here the documentation for the main framework and the plugins we use in this test suite:
   110  
   111   - [bats-core tutorial](https://bats-core.readthedocs.io/en/stable/tutorial.html)
   112   - [Writing tests](https://bats-core.readthedocs.io/en/stable/writing-tests.html)
   113   - [bats-assert](https://github.com/bats-core/bats-assert)
   114   - [bats-support](https://github.com/bats-core/bats-support)
   115   - [bats-file](https://github.com/bats-core/bats-file)
   116   - [bats-mock](https://github.com/grayhemp/bats-mock)
   117  
   118  > As it often happens with open source, the first results from search engines refer to the old, unmaintained forks.
   119  > Be sure to use the links above to find the good versions.
   120  
   121  Since bats-core is [TAP (Test Anything Protocol)](https://testanything.org/)
   122  compliant, its output is in a standardized format. It can be integrated with a
   123  separate [tap reporter](https://www.npmjs.com/package/tape#pretty-reporters) or
   124  included in a larger test suite. The TAP specification is pretty minimalist and
   125  some glue may be needed.
   126  
   127  
   128  # setup and teardown
   129  
   130  If you have read the bats-core tutorial linked above, you are aware of the
   131  `setup` and `teardown` functions.
   132  
   133  What you may have overlooked is that the script body outside the functions is
   134  executed multiple times, so we have to be careful of what we put there.
   135  
   136  Here we have a look at the execution flow with two tests:
   137  
   138  ```sh
   139  echo "begin" >&3
   140  
   141  setup_file() {
   142          echo "setup_file" >&3
   143  }
   144  
   145  teardown_file() {
   146          echo "teardown_file" >&3
   147  }
   148  
   149  setup() {
   150          echo "setup" >&3
   151  }
   152  
   153  teardown() {
   154          echo "teardown" >&3
   155  }
   156  
   157  @test "test 1" {
   158          echo "test #1" >&3
   159  }
   160  
   161  @test "test 2" {
   162          echo "test #2" >&3
   163  }
   164  
   165  echo "end" >&3
   166  ```
   167  
   168  The above test suite produces the following output:
   169  
   170  ```
   171  begin
   172  end
   173  setup_file
   174  begin
   175  end
   176   ✓ test 1
   177  setup
   178  test #1
   179  teardown
   180  begin
   181  end
   182   ✓ test 2
   183  setup
   184  test #2
   185  teardown
   186  teardown_file
   187  ```
   188  
   189  See how "begin" and "end" are repeated three times each? The code outside
   190  setup/teardown/test functions is really executed three times (more as you add
   191  more tests). You can put there variables or function definitions, but keep it
   192  to a minimum and [don't write anything to the standard
   193  output](https://bats-core.readthedocs.io/en/stable/writing-tests.html#code-outside-of-test-cases).
   194  For most things you want to use `setup_file()` instead.
   195  
   196  But.. there is a but. Quoting from [the FAQ](https://bats-core.readthedocs.io/en/stable/faq.html):
   197  
   198  > You can simply source <your>.sh files. However, be aware that source`ing
   199  > files with errors outside of any function (or inside `setup_file) will trip
   200  > up bats and lead to hard to diagnose errors. Therefore, it is safest to only
   201  > source inside setup or the test functions themselves.
   202  
   203  This doesn't mean you can't do that, just that you're on your own if the is an error.
   204  
   205  
   206  # Testing crowdsec
   207  
   208  ## Fixtures
   209  
   210  For the purpose of functional tests, crowdsec and its companions (cscli, plugin
   211  notifiers, bouncers) are installed in a local environment, which means tests
   212  should not install or touch anything outside a `./test/local` directory. This
   213  includes binaries, configuration files, databases, data downloaded from
   214  internet, logs... The use of `/tmp` is tolerated, but BATS also provides [three
   215  useful
   216  variables](https://bats-core.readthedocs.io/en/stable/writing-tests.html#special-variables):
   217  `$BATS_SUITE_TMPDIR`, `$BATS_FILE_TMPDIR` and `$BATS_TEST_TMPDIR` that let you
   218  ensure your desired level of isolation of temporary files across the tests.
   219  
   220  When built with `make bats-build`, the binaries will look there by default for
   221  their configuration and data needs. So you can run `./local/bin/cscli` from
   222  a shell with no need for further parameters.
   223  
   224  To set up the installation described above we provide a couple of scripts,
   225  `instance-data` and `instance-crowdsec`. They manage fixture and background
   226  processes; they are meant to be used in setup/teardown in several ways,
   227  according to the specific needs of the group of tests in the file.
   228  
   229   - `instance-data make`
   230  
   231     Creates a tar file in `./local-init/init-config-data.tar`.
   232     The file contains all the configuration, hub and database files needed
   233     to restore crowdsec to a known initial state.
   234     Things like `machines add ...`, `capi register`, `hub update`, `collections
   235     install crowdsecurity/linux` are executed here so they don't need to be
   236     repeated for each test or group of tests.
   237  
   238   - `instance-data load`
   239  
   240     Extracts the files created by `instance-data make` for use by the local
   241     crowdsec instance. Crowdsec must not be running while this operation is
   242     performed.
   243  
   244   - `instance-crowdsec [ start | stop ]`
   245  
   246     Runs (or stops) crowdsec as a background process. PID and lockfiles are
   247     written in `./local/var/run/`.
   248  
   249  
   250  Here are some ways to use these two scripts.
   251  
   252   - case 1: load a fresh crowsec instance + data for each test (01_base, 10_bouncers, 20_collections...)
   253  
   254      This offers the best isolation, but the tests run slower. More importantly,
   255      since there is no concept of "grouping" tests in bats-core with the exception
   256      of files, if you need to perform some setup that is common to two or more
   257      tests, you will have to repeat the code.
   258  
   259   - case 2: load a fresh set of data for each test, but run crowdsec only for
   260     the tests that need it, possibly after altering the configuration
   261     (02_nolapi, 03_noagent, 04_nocapi, 40_live-ban)
   262  
   263      This is useful because: 1) you sometimes don't want crowdsec to run at all,
   264      for example when testing `cscli` in isolation, or you may want to tweak the
   265      configuration inside the test function before running the lapi/agent. See
   266      how we use `yq` to change the YAML files to that effect.
   267  
   268   - case 3: start crowdsec with the initial set of configuration+data once, and keep it
   269     running for all the tests (50_simulation, 98_ipv4, 98_ipv6)
   270  
   271       This offers no isolation across tests, which over time could break more
   272       often as result, but you can rely on the test order to test more complex
   273       scenarios with a reasonable performance and the least amount of code.
   274  
   275  
   276  ## status, stdout and stderr
   277  
   278  As we said, if any error occurs inside a test function, the test
   279  fails immediately. You call `mycommand`, it exits with $? != 0, the test fails.
   280  
   281  But how to test the output, then? If we call `run mycommand`, then $? will be 0
   282  allowing the test to keep running. The real error status is stored in the
   283  `$status` variable, and the command output and standard error content are put
   284  together in the `$output` variable. By specifying `run --separate-stderr`, you
   285  can have separated `$output` and `$stderr` variables.
   286  
   287  The above is better explained in the bats-core tutorial. If you have not read it
   288  yet, now is a good time.
   289  
   290  For convenience, the `rune` function is an alias for `run --separate-stderr`, which
   291  can be used in most cases. For example, you don't want extraneous log messages in
   292  the way when you check the output of a command.
   293  
   294  The `$output` variable gets special treatment with the
   295  [bats-support](https://github.com/bats-core/bats-support) and
   296  [bats-assert][https://github.com/bats-core/bats-assert) plugins and can be
   297  checked with `assert_*` commands. The `$stderr` variable does not have these,
   298  but we can use `run echo "$stderr"` and then check `$output` with asserts.
   299  
   300  Remember that `run` always overwrites the `$output` variable, so if you consume
   301  it with `run jq <(output)` you can only do it once, because the second time it
   302  will read the output of the `jq` command. But you can construct a list of all
   303  the values you want and check them all in a single step.
   304  
   305  Note that `<(output)` is substituted with the file name of a file descriptor,
   306  so `mycmd <(output)` can become `mycmd /dev/fd/23`, `mycmd /tmp//sh-np.hpc7Zs`
   307  or `mycmd /proc/self/fd/38` depending on the platform. To have it fed to
   308  standard input, use `< <(output)`.
   309  
   310  See the `lib/*.sh` and `bats/*.bats` files for other tricks we employ.
   311  
   312  ## file operations
   313  
   314  We included the [bats-file](https://github.com/bats-core/bats-file) plugin to
   315  check the result of file system operations: existence, type/size/ownership checks
   316  on files, symlinks, directories, sockets.
   317  
   318  ## mocking external commands
   319  
   320  The [bats-mock](https://github.com/grayhemp/bats-mock) plugin allows you to define
   321  a "fake" behavior for the external commands called by a package under test, and
   322  to record and assert which parameters are passed to it.
   323  
   324  ## gotchas
   325  
   326   - pay attention to tests that are not run - for example "bats warning: Executed 143
   327     instead of expected 144 tests". They are especially tricky to debug.
   328  
   329   - using the `load` command in `teardown()` causes tests to be silently skipped or break in "funny"
   330     ways. The other functions seem safe.
   331  
   332  # Testing with MySQL and Postgres
   333  
   334  By default, the tests are run with the embedded sqlite database engine. This should be
   335  enough in most cases, since the database operations are abstracted via the `ent` ORM.
   336  
   337  You can however easily test with a different engine.
   338  
   339  ## Postgres
   340  
   341  Run Postgres somewhere, version 10 or above - easy to do in a docker container.
   342  
   343  You also need to install a postgresql-client package or equivalent, to provide
   344  recent pg_dump and pg_restore executables (not older than the PG version in the docker container).
   345  
   346  ```
   347  $ sudo docker run --detach --name=postgres -p 5432:5432 --env="POSTGRES_PASSWORD=postgres" postgres:latest
   348  ```
   349  
   350  The name of the container is not really important.
   351  If you are not using Docker, you may need to adjust the `PGHOST`/`PGPORT`/`PGPASSWORD`/`PGUSER` variables
   352  (defaults are 127.0.0.1, 5432, postgres, postgres).
   353  
   354  An additional user and database both named `crowdsec_test` will be created.
   355  
   356  Now you can build and run the tests (we skip bats-test-hub here, they really
   357  should not be affected by a change in DB).
   358  
   359  ```
   360  $ export DB_BACKEND=postgres
   361  $ make clean bats-build bats-fixture bats-test
   362  ```
   363  
   364  or with the pgx driver:
   365  
   366  ```
   367  $ export DB_BACKEND=pgx
   368  $ make clean bats-build bats-fixture bats-test
   369  ```
   370  
   371  The value of DB_BACKEND must not change between the build/fixture/test steps.
   372  
   373  ## MySQL/MariaDB
   374  
   375  Same considerations as above, with the following changes:
   376  
   377  ```
   378  $ sudo docker run --cap-add=sys_nice --detach --name=mysql -p 3306:3306  --env="MYSQL_ROOT_PASSWORD=password" mysql
   379  [...]
   380  $ export DB_BACKEND=mysql
   381  $ make clean bats-build bats-fixture bats-test
   382  ```
   383  
   384  or for MariaDB
   385  
   386  ```
   387  $ sudo docker run --cap-add=sys_nice --detach --name=mariadb -p 3306:3306  --env="MYSQL_ROOT_PASSWORD=password" mariadb
   388  ```
   389  
   390  A mysql-client package is required as well.
   391  
   392  ## troubleshooting
   393  
   394   - CAPI is disabled, why?
   395  Most tests don't need it. Helper scripts are provided in `test/enable-capi`
   396  and `test/disable-capi` for interactive use, and two library functions
   397  `config_enable_capi` and `config_disable_capi` to call inside the tests.
   398  You still need to call `cscli capi register` after enabling it.
   399  
   400   - My tests are hanging forever, why?
   401  See if you have a jq/yq or similar process waiting for standard input. Hint:
   402  you can pass a file from the result of the previous `run` command with
   403  `<(output)`. This substitutes the expression with a file name, but if you
   404  really want it in standard input, you have to use `< <(output)`. Bash is
   405  awesome but the syntax is often weird.
   406  
   407   - I can't do X with jq.
   408  If you prefer you can use yq. It can parse and generate json, and it has a
   409  different syntax.
   410  
   411   - I get "while parsing /tmp/....: yaml: line 5: mapping values are not allowed in this context"
   412  Check the heredocs (the <<EOT blocks). Each line must start with a hard TAB
   413  followed by spaces. You are probably missing some tabs.
   414  
   415  ## gotchas
   416  
   417   - Testing with Postgres or MySQL/MariaDB leads to (unpredictably) failing
   418     tests in the GitHub workflows, so we had to disable them by default. We do
   419     run these in a separate environment before doing releases. They should always
   420     pass if you run them in a development machine.
   421