Verified Commit e41b0860 authored by Luc Didry's avatar Luc Didry

Add a test suite

parent a4149b73
Pipeline #82847 passed with stages
in 3 minutes and 18 seconds
local/*
files/*
cover_db/*
lufi.conf
*.db
*.db-shm
......
image: hatsoftwares/lufi-test-ci:latest
stages:
- podcheck
- carton
- carton_bdd
- tests
before_script:
- rm -f *db
variables:
POSTGRES_DB: lufi_db
POSTGRES_USER: lufi
POSTGRES_PASSWORD: lufi_pwd
MYSQL_DATABASE: lufi_db
MYSQL_USER: lufi
MYSQL_PASSWORD: lufi_pwd
MYSQL_ROOT_PASSWORD: root
### Jobs templates
##
#
.carton_bdd_template: &carton_bdd_definition
stage: carton_bdd
retry: 2
artifacts:
paths:
- local/
expire_in: 1 week
dependencies:
- carton
.sqlite_template: &sqlite_definition
stage: tests
retry: 2
dependencies:
- carton_sqlite
services:
- name: rroemhild/test-openldap
alias: rroemhild-test-openldap
coverage: '/Total.* (\d+\.\d+)$/'
.pg_template: &pg_definition
stage: tests
retry: 2
dependencies:
- carton_postgresql
services:
- name: postgres:9.6
alias: postgres
- name: rroemhild/test-openldap
alias: rroemhild-test-openldap
coverage: '/Total .*(\d+\.\d+)$/'
.mysql_template: &mysql_definition
stage: tests
retry: 2
dependencies:
- carton_mysql
services:
- name: mariadb:10.1
alias: mariadb
- name: rroemhild/test-openldap
alias: rroemhild-test-openldap
coverage: '/Total .*(\d+\.\d+)$/'
### Podcheck
##
#
podcheck:
stage: podcheck
script:
- make podcheck
### Install common dependencies
##
#
carton:
stage: carton
artifacts:
paths:
- local/
expire_in: 1 week
dependencies: []
script:
- carton install --deployment --without=sqlite --without=postgresql
when: always
retry: 2
### Install DB related dependencies
##
#
carton_sqlite:
<<: *carton_bdd_definition
script:
- carton install --deployment --without=postgresql
carton_postgresql:
<<: *carton_bdd_definition
script:
- carton install --deployment --without=sqlite
#carton_mysql:
# <<: *carton_bdd_definition
# script:
# - carton install --deployment --without=sqlite --without=postgresql
### SQLite tests
##
#
sqlite:
<<: *sqlite_definition
script:
- MOJO_CONFIG=t/sqlite.conf make test
- MOJO_CONFIG=t/sqlite.conf make cover
### PostgreSQL tests
##
#
postgresql:
<<: *pg_definition
script:
- MOJO_CONFIG=t/postgresql.conf make test
- MOJO_CONFIG=t/postgresql.conf make cover
### MySQL tests
##
#
#mysql:
# <<: *mysql_definition
# script:
# - MOJO_CONFIG=t/mysql.conf make test
# - MOJO_CONFIG=t/mysql.conf make cover
......@@ -14,6 +14,7 @@ Revision history for Lufi
- Allow to choose your language
- Use a recurrent task to provision shorts
- Add a command to migrate data from SQLite to an other database
- Add a test suite
0.02.2 2017-09-18
- Fix cron tasks bug
......
......@@ -23,11 +23,15 @@ locales:
podcheck:
podchecker lib/Lufi/DB/File.pm lib/Lufi/DB/Slice.pm
cover:
PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='-MDevel::Cover' $(CARTON) cover --ignore_re '^local'
test:
$(CARTON) prove -l -f -o t/basic.t
@PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='-MDevel::Cover' $(CARTON) prove -l -f -o t/test.t
clean:
rm -rf lufi.db files/
dev: clean
$(CARTON) morbo $(LUFI) --listen http://0.0.0.0:3000 --watch lib/ --watch script/ --watch themes/ --watch lufi.conf
......
......@@ -21,6 +21,9 @@ requires 'Crypt::SaltedHash';
requires 'Data::Validate::URI';
requires 'Term::ProgressBar';
feature 'test' => sub {
requires 'Devel::Cover';
};
feature 'ldap', 'LDAP authentication support' => sub {
requires 'Net::LDAP';
requires 'Mojolicious::Plugin::Authentication';
......
......@@ -323,6 +323,65 @@ DISTRIBUTIONS
Data::Validate::Domain 0
Data::Validate::IP 0
ExtUtils::MakeMaker 0
Devel-Cover-1.31
pathname: P/PJ/PJCJ/Devel-Cover-1.31.tar.gz
provides:
Devel::Cover 1.31
Devel::Cover::Annotation::Git 1.31
Devel::Cover::Annotation::Random 1.31
Devel::Cover::Annotation::Svk 1.31
Devel::Cover::Branch 1.31
Devel::Cover::Collection 1.31
Devel::Cover::Collection::Template::Provider 1.31
Devel::Cover::Condition 1.31
Devel::Cover::Condition_and_2 1.31
Devel::Cover::Condition_and_3 1.31
Devel::Cover::Condition_or_2 1.31
Devel::Cover::Condition_or_3 1.31
Devel::Cover::Condition_xor_4 1.31
Devel::Cover::Criterion 1.31
Devel::Cover::DB 1.31
Devel::Cover::DB::Criterion 1.31
Devel::Cover::DB::Digests 1.31
Devel::Cover::DB::File 1.31
Devel::Cover::DB::IO 1.31
Devel::Cover::DB::IO::Base 1.31
Devel::Cover::DB::IO::JSON 1.31
Devel::Cover::DB::IO::Sereal 1.31
Devel::Cover::DB::IO::Storable 1.31
Devel::Cover::DB::Run 1.31
Devel::Cover::DB::Structure 1.31
Devel::Cover::Html_Common 1.31
Devel::Cover::Op 1.31
Devel::Cover::Pod 1.31
Devel::Cover::Report::Compilation 1.31
Devel::Cover::Report::Html 1.31
Devel::Cover::Report::Html_basic 1.31
Devel::Cover::Report::Html_basic::Template::Provider 1.31
Devel::Cover::Report::Html_minimal 1.31
Devel::Cover::Report::Html_subtle 1.31
Devel::Cover::Report::Html_subtle::Template::Provider 1.31
Devel::Cover::Report::Json 1.31
Devel::Cover::Report::Sort 1.31
Devel::Cover::Report::Text 1.31
Devel::Cover::Report::Text2 1.31
Devel::Cover::Report::Vim 1.31
Devel::Cover::Report::Vim::Template::Provider 1.31
Devel::Cover::Statement 1.31
Devel::Cover::Subroutine 1.31
Devel::Cover::Test 1.31
Devel::Cover::Time 1.31
Devel::Cover::Truth_Table 1.31
Devel::Cover::Truth_Table::Row 1.31
Devel::Cover::Util 1.31
Devel::Cover::Web 1.31
requirements:
B::Debug 0
Digest::MD5 0
ExtUtils::MakeMaker 0
HTML::Entities 3.69
Storable 0
Test::More 0
Devel-GlobalDestruction-0.14
pathname: H/HA/HAARG/Devel-GlobalDestruction-0.14.tar.gz
provides:
......
......@@ -143,7 +143,7 @@ sub startup {
# Logout page
$r->get('/logout')
->to('Auth#logout')
->to('Auth#log_out')
->name('logout');
}
......
......@@ -26,7 +26,7 @@ sub login {
}
}
sub logout {
sub log_out {
my $c = shift;
if ($c->is_user_authenticated) {
......
......@@ -200,6 +200,13 @@ sub upload {
$c->app->log->debug('Client disconnected');
}
);
} else {
$c->on(
message => sub {
$c->app->log->info(sprintf('Someone unauthenticated tried to upload a file. IP: %s', $c->ip));
$c->finish;
}
);
}
}
......
......@@ -431,6 +431,28 @@ sub delete_creator_before {
$c->app->dbi->db->query('UPDATE files SET created_by = NULL WHERE created_by IS NOT NULL AND created_at < ?', $separation);
}
=head2 delete_all
=over 1
=item B<Usage> : C<$c-E<gt>delete_all()>
=item B<Arguments> : none
=item B<Purpose> : delete all file records from database unconditionnally
=item B<Returns> : nothing
=back
=cut
sub delete_all {
my $c = shift;
$c->app->dbi->db->delete('files');
}
=head2 _slurp
=over 1
......
......@@ -136,6 +136,28 @@ sub get_slices_of_file {
return c(@slices);
}
=head2 delete_all
=over 1
=item B<Usage> : C<$c-E<gt>delete_all()>
=item B<Arguments> : none
=item B<Purpose> : delete all file records from database unconditionnally
=item B<Returns> : nothing
=back
=cut
sub delete_all {
my $c = shift;
$c->app->dbi->db->delete('slices');
}
=head2 _slurp
=over 1
......
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('Lufi');
$t->get_ok('/')->status_is(200)->content_like(qr/Lufi/i);
done_testing();
luc:$apr1$zG4UAKGa$FqSi4widrkVH/pT3qPawd.
This diff is collapsed.
This diff is collapsed.
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
use Mojo::Base -strict;
use Mojo::File;
use Mojo::JSON qw(to_json from_json true);
use Mojolicious;
use Test::More;
use Test::Mojo;
use Lufi::DB::File;
use Lufi::DB::Slice;
use FindBin qw($Bin);
my ($m, $cfile, $config_orig, $config_file, $config_content);
my $msg = to_json {
"total" => 1,
"part" => 0,
"size" => 7,
"name" => "foobar.txt",
"type" => "text/plain",
"delay" => "0",
"del_at_first_view" => 0,
"id" => undef,
"i" => 0
};
my $encrypted = '"{\\"iv\\":\\"2RGAviAeYybBqcLCmnqlgA==\\",\\"v\\":1,\\"iter\\":10000,\\"ks\\":128,\\"ts\\":64,\\"mode\\":\\"ccm\\",\\"adata\\":\\"\\",\\"cipher\\":\\"aes\\",\\"salt\\":\\"1dvKtbZ8hxA=\\",\\"ct\\":\\"w9wDZCwNSyH/yL7q1GW5fPSdi+w=\\"}"';
my $encrypted_rgx = $encrypted;
$encrypted_rgx =~ s@\\@\\\\@g;
$encrypted_rgx =~ s@\+@\\+@g;
$encrypted_rgx =~ s@(\{|\})@\\$1@g;
BEGIN {
use lib 'lib';
$m = Mojolicious->new;
$cfile = Mojo::File->new($Bin, '..', 'lutim.conf');
if (defined $ENV{MOJO_CONFIG}) {
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
unless (-e $cfile->to_abs) {
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
}
}
my $config = $m->plugin(
'Config' => {
file => $cfile->to_abs->to_string,
default => {
prefix => '/',
provisioning => 100,
provis_step => 5,
length => 10,
token_length => 32,
secrets => ['hfudsifdsih'],
default_delay => 0,
max_delay => 0,
mail => {
how => 'sendmail'
},
mail_sender => 'no-reply@lufi.io',
theme => 'default',
upload_dir => 'files',
session_duration => 3600,
allow_pwd_on_files => 0,
dbtype => 'sqlite',
db_path => 'lufi.db',
force_burn_after_reading => 0,
x_frame_options => 'DENY',
x_content_type_options => 'nosniff',
x_xss_protection => '1; mode=block',
}
}
);
$m->plugin('Lufi::Plugin::Helpers');
$m->plugin('DebugDumperHelper');
} ## end BEGIN
Lufi::DB::Slice->new(app => $m)->delete_all;
Lufi::DB::File->new(app => $m)->delete_all;
my $t = Test::Mojo->new('Lufi');
## Wait for short generation
sleep 3;
## Let's go
$t->get_ok('/')
->status_is(200)
->content_like(qr@Lufi@i);
test_upload_file();
test_download_file();
## Test htpasswd
switch_to_htpasswd();
auth_test_suite('luc', 'toto');
restore_config();
## Test LDAP
switch_to_ldap();
auth_test_suite('zoidberg', 'zoidberg');
restore_config();
done_testing();
######
### Functions
##
sub test_upload_file {
$t->websocket_ok('/upload/')
->send_ok($msg.'XXMOJOXX'.$encrypted)
->message_ok
->message_like(qr@"created_at":\d+@)
->message_like(qr@"del_at_first_view":false@)
->message_like(qr@"delay":"0"@)
->message_like(qr@"duration":0@)
->message_like(qr@"i":0@)
->message_like(qr@"j":0@)
->message_like(qr@"name":"foobar\.txt"@)
->message_like(qr@"parts":1@)
->message_like(qr@"sent_delay":"0"@)
->message_like(qr@"short":"[^"]+"@)
->message_like(qr@"size":7@)
->message_like(qr@"success":true@)
->message_like(qr@"token":"[^"]+"}@)
->finish_ok;
}
sub test_download_file {
my $ws_msg;
$t->ua->websocket_p('/upload/')->then(sub {
my $tx = shift;
my $promise = Mojo::Promise->new;
$tx->on(finish => sub { $promise->resolve });
$tx->on(message => sub {
my $tx = shift;
$ws_msg = shift;
$tx->finish;
});
$tx->send($msg.'XXMOJOXX'.$encrypted);
return $promise;
})->catch(sub {
my $err = shift;
is($err, undef);
})->wait;
$ws_msg = from_json($ws_msg);
$t->websocket_ok('/download/'.$ws_msg->{short})
->send_ok(to_json({part => 0}))
->message_ok
->message_like(qr@"total":1@)
->message_like(qr@"part":0@)
->message_like(qr@"i":0@)
->message_like(qr@"id":null@)
->message_like(qr@"del_at_first_view":0@)
->message_like(qr@"delay":"0"@)
->message_like(qr@"name":"foobar\.txt"@)
->message_like(qr@"size":7@)
->message_like(qr@"type":"text\\/plain"@)
->message_like(qr@XXMOJOXX@)
->message_like(qr@$encrypted_rgx@)
->send_ok(to_json({ended => true}))
->finish_ok;
}
sub auth_test_suite {
my ($login, $pass) = @_;
$t->get_ok('/')
->status_is(302)
->header_is(Location => '/login');
test_fail_upload();
test_login($login, $pass);
test_upload_file();
test_download_file();
$t->get_ok('/logout')
->status_is(200)
->content_like(qr@You have been successfully logged out\.@);
test_fail_upload();
}
sub test_fail_upload {
# An empty message would make it fail if we were allowed to go in the authenticated part
$t->websocket_ok('/upload/')
->send_ok('')
->finish_ok;
}
sub test_login {
my ($login, $pass) = @_;
$t->get_ok('/login')
->status_is(200)
->content_like(qr@Signin@);
$t->post_ok('/login' => form => { login => $login, password => $pass })
->status_is(302)
->header_is(Location => '/');
$t->get_ok('/login')
->status_is(302)
->header_is(Location => '/');
}
sub restore_config {
$config_file->spurt($config_orig);
}
sub switch_to_htpasswd {
$config_file = Mojo::File->new($cfile->to_abs->to_string);
$config_content = $config_file->slurp;
$config_orig = $config_content;
$config_content =~ s/#?htpasswd.*/htpasswd => 't\/lufi.passwd',/gm;
$config_file->spurt($config_content);
Lufi::DB::Slice->new(app => $m)->delete_all;
Lufi::DB::File->new(app => $m)->delete_all;
$t = Test::Mojo->new('Lufi');
## Wait for short generation
sleep 3;
}
sub switch_to_ldap {
$config_content = $config_orig;
$config_content =~ s/^( +)#?ldap => \{ uri/$1ldap => { uri/gm;
$config_file->spurt($config_content);
Lufi::DB::Slice->new(app => $m)->delete_all;
Lufi::DB::File->new(app => $m)->delete_all;
$t = Test::Mojo->new('Lufi');
## Wait for short generation
sleep 3;
}
......@@ -93,15 +93,15 @@ msgstr "Copia tots els enllaços al porta-retalls"
msgid "Copy to clipboard"
msgstr "Copia al porta-retalls"
#: lib/Lufi/Controller/Files.pm:453
#: lib/Lufi/Controller/Files.pm:460
msgid "Could not delete the file. You are not authenticated."
msgstr "No es pot esborrar el fitxer. No esteu autenticat."
#: lib/Lufi/Controller/Files.pm:435
#: lib/Lufi/Controller/Files.pm:442
msgid "Could not find the file. Are you sure of the URL and the token?"
msgstr "No es troba el fitxer. Esteu segur de la URL i el testimoni?"
#: lib/Lufi/Controller/Files.pm:346
#: lib/Lufi/Controller/Files.pm:353
msgid "Could not find the file. Are you sure of the URL?"
msgstr "No trobo el fitxer. Esteu segurs de la URL?"
......@@ -161,15 +161,15 @@ msgstr "correus electrònics"
msgid "Encrypting part XX1 of XX2"
msgstr "S'està xifrant la part XX1 de XX2"
#: lib/Lufi/Controller/Files.pm:235
#: lib/Lufi/Controller/Files.pm:242
msgid "Error: the file existed but was deleted."
msgstr "Error: el fitxer existia però va ser eliminat."
#: lib/Lufi/Controller/Files.pm:315
#: lib/Lufi/Controller/Files.pm:322
msgid "Error: the file has not been sent entirely."
msgstr "Error: el fitxer no s'ha enviat del tot."
#: lib/Lufi/Controller/Files.pm:325
#: lib/Lufi/Controller/Files.pm:332
msgid "Error: unable to find the file. Are you sure of your URL?"
msgstr "Error: no trobo el fitxer. Esteu segur de la URL ?"
......@@ -185,7 +185,7 @@ msgstr "Expira el"
msgid "Export localStorage data"
msgstr "Exporta dades a l'emmagatzematge local"
#: lib/Lufi/Controller/Files.pm:417
#: lib/Lufi/Controller/Files.pm:424
msgid "File deleted"
msgstr "Fitxer eliminat"
......@@ -383,7 +383,7 @@ msgstr "El cos del correu no pot estar buit."
msgid "The email subject can't be empty."
msgstr "L'assumpte dle correu no pot estar buit."
#: lib/Lufi/Controller/Files.pm:414
#: lib/Lufi/Controller/Files.pm:421
msgid "The file has already been deleted"
msgstr "El fitxer ja ha estat esborrat"
......@@ -412,7 +412,7 @@ msgstr "L'autor original (i per ara l'únic) és <a href=\"https://fiat-tux.fr\"
msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator."
msgstr "El servidor no ha pogut trobar el registre del fitxer per afegir-hi el tros del fitxer. Si us plau, contacteu l'administrador."
#: lib/Lufi/Controller/Files.pm:241
#: lib/Lufi/Controller/Files.pm:248
msgid "This file has been deactivated by the admins. Contact them to know why."
msgstr ""
......@@ -425,17 +425,17 @@ msgid "Unable to copy the link(s) to your clipboard"
msgstr "No s'han pogut copiar l'enllaç o els enllaços al porta-retalls."
#. ($short)
#: lib/Lufi/Controller/Files.pm:385
#: lib/Lufi/Controller/Files.pm:392
msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
msgstr "No he pogut obtenir el comptador de %1. El fitxer no existeix. Serà eliminat del teu emmagatzematge local."
#. ($short)
#: lib/Lufi/Controller/Files.pm:375
#: lib/Lufi/Controller/Files.pm:382
msgid "Unable to get counter for %1. The token is invalid."
msgstr "No he pogut obtenir el comptador de %1. El testimoni no és vàlid."
#. ($short)
#: lib/Lufi/Controller/Files.pm:395
#: lib/Lufi/Controller/Files.pm:402
msgid "Unable to get counter for %1. You are not authenticated."
msgstr "No he pogut obtenir el comptador de %1. No esteu autenticat."
......@@ -500,7 +500,7 @@ msgstr ""
msgid "Your file is too big: %1 (maximum size allowed: %2)"
msgstr "El fitxer és massa gran: %1 (mida màxima admesa: %2)"
#: lib/Lufi/Controller/Files.pm:297
#: lib/Lufi/Controller/Files.pm:304
msgid "Your password is not valid. Please refresh the page to retry."
msgstr ""
......
......@@ -90,15 +90,15 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""
#: lib/Lufi/Controller/Files.pm:453
#: lib/Lufi/Controller/Files.pm:460
msgid "Could not delete the file. You are not authenticated."
msgstr ""
#: lib/Lufi/Controller/Files.pm:435
#: lib/Lufi/Controller/Files.pm:442
msgid "Could not find the file. Are you sure of the URL and the token?"
msgstr ""
#: lib/Lufi/Controller/Files.pm:346
#: lib/Lufi/Controller/Files.pm:353
msgid "Could not find the file. Are you sure of the URL?"
msgstr ""
......@@ -158,15 +158,15 @@ msgstr ""
msgid "Encrypting part XX1 of XX2"
msgstr ""
#: lib/Lufi/Controller/Files.pm:235
#: lib/Lufi/Controller/Files.pm:242
msgid "Error: the file existed but was deleted."
msgstr ""
#: lib/Lufi/Controller/Files.pm:315
#: lib/Lufi/Controller/Files.pm:322
msgid "Error: the file has not been sent entirely."
msgstr ""
#: lib/Lufi/Controller/Files.pm:325
#: lib/Lufi/Controller/Files.pm:332
msgid "Error: unable to find the file. Are you sure of your URL?"
msgstr ""
......@@ -182,7 +182,7 @@ msgstr ""
msgid "Export localStorage data"
msgstr ""
#: lib/Lufi/Controller/Files.pm:417
#: lib/Lufi/Controller/Files.pm:424
msgid "File deleted"
msgstr ""
......@@ -377,7 +377,7 @@ msgstr ""
msgid "The email subject can't be empty."
msgstr ""
#: lib/Lufi/Controller/Files.pm:414
#: lib/Lufi/Controller/Files.pm:421
msgid "The file has already been deleted"
msgstr ""
......@@ -406,7 +406,7 @@ msgstr ""
msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator."
msgstr ""
#: lib/Lufi/Controller/Files.pm:241
#: lib/Lufi/Controller/Files.pm:248
msgid "This file has been deactivated by the admins. Contact them to know why."
msgstr ""
......@@ -419,17 +419,17 @@ msgid "Unable to copy the link(s) to your clipboard"
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:385
#: lib/Lufi/Controller/Files.pm:392
msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:375
#: lib/Lufi/Controller/Files.pm:382
msgid "Unable to get counter for %1. The token is invalid."
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:395
#: lib/Lufi/Controller/Files.pm:402
msgid "Unable to get counter for %1. You are not authenticated."