Remove unwanted file

This commit is contained in:
Knah Tsaeb 2024-12-10 16:52:55 +01:00
parent e225d3ebbe
commit 8ee7580ff2
180 changed files with 0 additions and 37275 deletions

View file

@ -1,12 +0,0 @@
module.exports = {
"extends": "airbnb-base",
"env": {
"browser": true,
},
"rules": {
"no-param-reassign": 0, // manipulate DOM style properties
"no-restricted-globals": 0, // currently Shaarli uses alert/confirm, could be be improved later
"no-alert": 0, // currently Shaarli uses alert/confirm, could be be improved later
"no-cond-assign": [2, "except-parens"], // assignment in while loops is readable and avoid assignment duplication
}
};

View file

@ -1,15 +0,0 @@
module.exports = {
extends: 'stylelint-config-standard',
plugins: [
"stylelint-scss"
],
rules: {
"indentation": [2],
"number-leading-zero": null,
// Replace CSS @ with SASS ones
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
// not compatible with SASS apparently
"no-descending-specificity": null
},
}

View file

@ -1,13 +0,0 @@
<IfModule version_module>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
<IfVersion < 2.4>
Allow from none
Deny from all
</IfVersion>
</IfModule>
<IfModule !version_module>
Require all denied
</IfModule>

View file

@ -1,60 +0,0 @@
user nginx nginx;
daemon off;
worker_processes 4;
pid /var/run/nginx.pid;
events {
worker_connections 768;
}
http {
include mime.types;
default_type application/octet-stream;
keepalive_timeout 20;
client_max_body_size 10m;
index index.html index.php;
server {
listen 80;
listen [::]:80;
root /var/www/shaarli;
access_log /var/log/nginx/shaarli.access.log;
error_log /var/log/nginx/shaarli.error.log;
location ~* \.(?:ico|css|js|gif|jpe?g|png|ttf|oet|woff2?)$ {
# cache static assets
expires max;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location = /favicon.ico {
# serve the Shaarli favicon from its custom location
alias /var/www/shaarli/images/favicon.ico;
}
location /doc/html/ {
default_type "text/html";
try_files $uri $uri/ $uri.html =404;
}
location / {
# Slim - rewrite URLs & do NOT serve static files through this location
try_files _ /index.php$is_args$args;
}
location ~ index\.php$ {
# Slim - split URL path into (script_filename, path_info)
try_files $uri =404;
fastcgi_split_path_info ^(index.php)(/.+)$;
# filter and proxy PHP requests to PHP-FPM
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
}
}

View file

@ -1,16 +0,0 @@
[global]
daemonize = no
[www]
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx
catch_workers_output = yes
listen = /var/run/php-fpm.sock
pm = dynamic
pm.max_children = 20
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 2048

View file

@ -1,2 +0,0 @@
#!/bin/sh
/bin/true

View file

@ -1,2 +0,0 @@
#!/bin/execlineb -P
nginx

View file

@ -1,2 +0,0 @@
#!/bin/execlineb -P
php-fpm8 -F

View file

@ -1,64 +0,0 @@
# Docker-ignore
.dev
.git
.github
.gitattributes
.gitignore
tests
# Docker related resources are not needed inside the container
.dockerignore
Dockerfile
Dockerfile.armhf
# Docker Compose resources
docker-compose.yml
# Shaarli runtime resources
cache/*
data/*
pagecache/*
tmp/*
# Shaarli's docs are created during the build
doc/html/
# Eclipse project files
.settings
.buildpath
.project
# Raintpl generated pages
*.rtpl.php
# 3rd-party dependencies
vendor/
# Release archives
*.tar.gz
*.zip
inc/languages/*/LC_MESSAGES/shaarli.mo
# Development and test resources
coverage
doxygen
sandbox
phpmd.html
# User plugin configuration
plugins/*/config.php
# 3rd party themes
tpl/*
!tpl/default
!tpl/vintage
# Front end
node_modules
tpl/default/js
tpl/default/css
tpl/default/fonts
tpl/default/img
tpl/vintage/js
tpl/vintage/css
tpl/vintage/img

View file

@ -1,23 +0,0 @@
# EditorConfig: http://EditorConfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[*.{htaccess,html,scss,js,json,xml,yml}]
indent_size = 2
[*.php]
max_line_length = 120
[Dockerfile]
max_line_length = 80
[Makefile]
indent_style = tab

47
.gitattributes vendored
View file

@ -1,47 +0,0 @@
# Set default behavior
* text=auto eol=lf
# Ensure sources are processed
*.conf text
*.css text
*.html text diff=html
*.js text
*.md text
*.php text diff=php
Dockerfile text
# Do not alter images nor minified scripts nor fonts
*.ico binary
*.jpg binary
*.png binary
*.svg binary
*.otf binary
*.eot binary
*.woff binary
*.woff2 binary
*.ttf binary
*.min.css binary
*.min.js binary
*.mo binary
# Exclude from Git archives
.editorconfig export-ignore
.dev export-ignore
.gitattributes export-ignore
.github export-ignore
.gitignore export-ignore
.travis.yml export-ignore
doc/**/*.json export-ignore
doc/**/*.md export-ignore
.docker/ export-ignore
.dockerignore export-ignore
docker-compose.* export-ignore
Dockerfile* export-ignore
Doxyfile export-ignore
Makefile export-ignore
node_modules/ export-ignore
doc/conf.py export-ignore
doc/requirements.txt export-ignore
doc/html/.doctrees/ export-ignore
phpunit.xml export-ignore
tests/ export-ignore

22
.github/mailmap vendored
View file

@ -1,22 +0,0 @@
ArthurHoaro <arthur@hoa.ro> <arthur.hoareau@wizacha.com>
ArthurHoaro <arthur@hoa.ro> Arthur
Florian Eula <eula.florian@gmail.com> feula
Florian Eula <eula.florian@gmail.com> <mr.pikzen@gmail.com>
Immánuel Fodor <immanuelfactor+github@gmail.com>
Immánuel Fodor <immanuelfactor+github@gmail.com> Immánuel! <21174107+immanuelfodor@users.noreply.github.com>
kalvn <kalvnthereal@gmail.com> <kalvn@users.noreply.github.com>
kalvn <kalvnthereal@gmail.com> <kalvn@pm.me>
Neros <contact@neros.fr> <NerosTie@users.noreply.github.com>
Nicolas Danelon <hi@nicolasmd.com.ar> nicolasm
Nicolas Danelon <hi@nicolasmd.com.ar> <nda@3818.com.ar>
Nicolas Danelon <hi@nicolasmd.com.ar> <nicolasdanelon@gmail.com>
Nicolas Danelon <hi@nicolasmd.com.ar> <nicolasdanelon@users.noreply.github.com>
Sébastien Sauvage <sebsauvage@sebsauvage.net>
Sébastien NOBILI <code@pipoprods.org> <s-code-github@pipoprods.org>
Timo Van Neerden <fire@lehollandaisvolant.net>
Timo Van Neerden <fire@lehollandaisvolant.net> lehollandaisvolant <levoltigeurhollandais@gmail.com>
VirtualTam <virtualtam@flibidi.net> <tamisier.aurelien@gmail.com>
VirtualTam <virtualtam@flibidi.net> <virtualtam+github@flibidi.net>
VirtualTam <virtualtam@flibidi.net> <virtualtam@flibidi.org>
Willi Eggeling <thewilli@gmail.com> <mail@wje-online.de>
Willi Eggeling <thewilli@gmail.com> <thewilli@users.noreply.github.com>

View file

@ -1,106 +0,0 @@
name: Shaarli CI
on: [push, pull_request]
jobs:
php:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1', '8.2']
name: PHP ${{ matrix.php-versions }}
steps:
- name: Set locales
run: |
sudo locale-gen de_DE.utf8 && \
sudo locale-gen en_US.utf8 && \
sudo locale-gen fr_FR.utf8 && \
sudo dpkg-reconfigure --frontend=noninteractive locales
- name: Install Gettext
run: sudo apt-get install gettext
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: gd, xml, curl, mbstring, intl, gettext
tools: composer:v2
- name: Check PHP version
run: php -v
- name: Setup Composer from PHP version + update
run: composer config --unset platform && composer config platform.php ${{ matrix.php-versions }}
- name: Update dependencies for PHP 8.x
if: ${{ matrix.php-versions == '8.0' || matrix.php-versions == '8.1' }}
run: |
composer update && \
composer remove --dev phpunit/phpunit && \
composer require --dev phpunit/php-text-template ^2.0 && \
composer require --dev phpunit/phpunit ^9.0
- name: Update dependencies for PHP 7.x
if: ${{ matrix.php-versions != '8.0' && matrix.php-versions != '8.1' }}
run: composer update
- name: Clean up
run: make clean
- name: Check permissions
run: make check_permissions
- name: Run PHPCS
run: make code_sniffer
- name: Run tests
run: make all_tests
node:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '14.x'
- name: Yarn install
run: yarnpkg install
- name: Verify successful frontend builds
run: yarnpkg run build
- name: JS static analysis
run: make eslint
- name: Linter for SASS syntax
run: make sasslint
python:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Build documentation
run: make htmldoc
trivy-repo:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Run trivy scanner on repository (non-blocking)
run: make test_trivy_repo TRIVY_EXIT_CODE=0

View file

@ -1,45 +0,0 @@
name: Build/push Docker image (master/latest)
on:
push:
branches: [ master ]
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v3
- name: Set shaarli version to the latest commit hash
run: sed -i "s/dev/$(git rev-parse --short HEAD)/" shaarli_version.php
- name: Build and push
id: docker_build
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: |
${{ secrets.DOCKER_IMAGE }}:latest
ghcr.io/${{ secrets.DOCKER_IMAGE }}:latest
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Run trivy scanner on latest docker image
run: make test_trivy_docker TRIVY_TARGET_DOCKER_IMAGE=ghcr.io/${{ secrets.DOCKER_IMAGE }}:latest

View file

@ -1,21 +0,0 @@
name: Build Docker image (Pull Request)
on:
pull_request:
branches: [ master ]
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build Docker image
id: docker_build
uses: docker/build-push-action@v2
with:
push: false
tags: shaarli/shaarli:pr-${{ github.event.number }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View file

@ -1,43 +0,0 @@
name: Build/push Docker image (tags/releases)
on:
push:
tags:
- "v*.*.*"
branches:
- "v*.*"
- release
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- name: Get the tag name
run: echo "REF=${GITHUB_REF##*/}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v3
with:
push: true
platforms: linux/amd64,linux/arm/v7
tags: |
${{ secrets.DOCKER_IMAGE }}:${{ env.REF }}
ghcr.io/${{ secrets.DOCKER_IMAGE }}:${{ env.REF }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View file

@ -1,74 +0,0 @@
# Stage 1:
# - Copy Shaarli sources
# - Build documentation
FROM python:3-alpine as docs
ADD . /usr/src/app/shaarli
RUN cd /usr/src/app/shaarli \
&& apk add --no-cache gcc musl-dev make bash \
&& make htmldoc
# Stage 2:
# - Resolve PHP dependencies with Composer
FROM composer:latest as composer
COPY --from=docs /usr/src/app/shaarli /app/shaarli
RUN cd shaarli \
&& composer --prefer-dist --no-dev install
# Stage 3:
# - Frontend dependencies
FROM node:12-alpine as node
COPY --from=composer /app/shaarli shaarli
RUN cd shaarli \
&& yarnpkg install \
&& yarnpkg run build \
&& rm -rf node_modules
# Stage 4:
# - Shaarli image
FROM alpine:3.16.7
LABEL maintainer="Shaarli Community"
RUN apk --update --no-cache add \
ca-certificates \
nginx \
php8 \
php8-ctype \
php8-curl \
php8-fpm \
php8-gd \
php8-gettext \
php8-iconv \
php8-intl \
php8-json \
php8-ldap \
php8-mbstring \
php8-openssl \
php8-session \
php8-xml \
php8-simplexml \
php8-zlib \
s6
COPY .docker/nginx.conf /etc/nginx/nginx.conf
COPY .docker/php-fpm.conf /etc/php8/php-fpm.conf
COPY .docker/services.d /etc/services.d
RUN rm -rf /etc/php8/php-fpm.d/www.conf \
&& sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php8/php.ini \
&& sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php8/php.ini
WORKDIR /var/www
COPY --from=node /shaarli shaarli
RUN chown -R nginx:nginx . \
&& ln -sf /dev/stdout /var/log/nginx/shaarli.access.log \
&& ln -sf /dev/stderr /var/log/nginx/shaarli.error.log
VOLUME /var/www/shaarli/cache
VOLUME /var/www/shaarli/data
EXPOSE 80
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
CMD []

215
Makefile
View file

@ -1,215 +0,0 @@
# The personal, minimalist, super fast, database-free, bookmarking service.
# Makefile for PHP code analysis & testing, documentation and release generation
BIN = vendor/bin
all: check_permissions test
##
# Docker test adapter
#
# Shaarli sources and vendored libraries are copied from a shared volume
# to a user-owned directory to enable running tests as a non-root user.
##
docker_%:
rsync -az /shaarli/ ~/shaarli/
cd ~/shaarli && make $*
##
# PHP_CodeSniffer
# Detects PHP syntax errors
# Documentation (usage, output formatting):
# - http://pear.php.net/manual/en/package.php.php-codesniffer.usage.php
# - http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php
##
PHPCS := $(BIN)/phpcs
# Use GNU Tar where available
ifneq (, $(shell which gtar))
TAR := gtar
else
TAR := tar
endif
code_sniffer:
@$(PHPCS)
### - errors by Git author
code_sniffer_blame:
@$(PHPCS) --report-gitblame
### - all errors/warnings
code_sniffer_full:
@$(PHPCS) --report-full --report-width=200
### - errors grouped by kind
code_sniffer_source:
@$(PHPCS) --report-source || exit 0
##
# Checks source file & script permissions
##
check_permissions:
@echo "----------------------"
@echo "Check file permissions"
@echo "----------------------"
@for file in `git ls-files | grep -v docker`; do \
if [ -x $$file ]; then \
errors=true; \
echo "$${file} is executable"; \
fi \
done; [ -z $$errors ] || false
##
# PHPUnit
# Runs unitary and functional tests
# Generates an HTML coverage report if Xdebug is enabled
#
# See phpunit.xml for configuration
# https://phpunit.de/manual/current/en/appendixes.configuration.html
##
test: translate
@echo "-------"
@echo "PHPUNIT"
@echo "-------"
@mkdir -p sandbox coverage
@$(BIN)/phpunit --coverage-php coverage/main.cov --bootstrap tests/bootstrap.php --testsuite unit-tests
locale_test_%:
@UT_LOCALE=$*.utf8 \
$(BIN)/phpunit \
--coverage-php coverage/$(firstword $(subst _, ,$*)).cov \
--bootstrap tests/languages/bootstrap.php \
--testsuite language-$(firstword $(subst _, ,$*))
all_tests: test locale_test_de_DE locale_test_en_US locale_test_fr_FR
@# --The current version is not compatible with PHP 7.2
@#$(BIN)/phpcov merge --html coverage coverage
@# --text doesn't work with phpunit 4.* (v5 requires PHP 5.6)
@#$(BIN)/phpcov merge --text coverage/txt coverage
### download 3rd-party PHP libraries, including dev dependencies
composer_dependencies_dev: clean
composer install --prefer-dist
##
# Custom release archive generation
#
# For each tagged revision, GitHub provides tar and zip archives that correspond
# to the output of git-archive
#
# These targets produce similar archives, featuring 3rd-party dependencies
# to ease deployment on shared hosting.
##
ARCHIVE_VERSION := shaarli-$$(git describe)-full
ARCHIVE_PREFIX=Shaarli/
release_archive: release_tar release_zip
### download 3rd-party PHP libraries
composer_dependencies: clean
composer install --no-dev --prefer-dist
find vendor/ -name ".git" -type d -exec rm -rf {} +
### download 3rd-party frontend libraries
frontend_dependencies:
yarnpkg install
### Build frontend dependencies
build_frontend: frontend_dependencies
yarnpkg run build
### generate a release tarball and include 3rd-party dependencies and translations
release_tar: composer_dependencies htmldoc translate build_frontend
git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD
$(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/
$(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/
$(TAR) rvf $(ARCHIVE_VERSION).tar --transform "s|^tpl|$(ARCHIVE_PREFIX)tpl|" tpl/
gzip $(ARCHIVE_VERSION).tar
### generate a release zip and include 3rd-party dependencies and translations
release_zip: composer_dependencies htmldoc translate build_frontend
git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD
mkdir -p $(ARCHIVE_PREFIX)/doc
mkdir -p $(ARCHIVE_PREFIX)/vendor
rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/
rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/
rsync -a tpl/ $(ARCHIVE_PREFIX)tpl/
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)tpl/
rm -rf $(ARCHIVE_PREFIX)
##
# Targets for repository and documentation maintenance
##
### remove all unversioned files
clean:
@git clean -df
@rm -rf sandbox trivy*
### generate the AUTHORS file from Git commit information
generate_authors:
@cp .github/mailmap .mailmap
@git shortlog -sne > AUTHORS
@rm .mailmap
### generate phpDocumentor documentation
phpdoc: clean
@docker run --rm -v $(PWD):/data -u `id -u`:`id -g` phpdoc/phpdoc
### generate HTML documentation from Markdown pages with Sphinx
htmldoc:
python3 -m venv venv/
bash -c 'source venv/bin/activate; \
pip install wheel; \
pip install sphinx==7.1.0 furo==2023.7.26 myst-parser sphinx-design; \
sphinx-build -b html -c doc/ doc/md/ doc/html/'
find doc/html/ -type f -exec chmod a-x '{}' \;
rm -r venv
### Generate Shaarli's translation compiled file (.mo)
translate:
@echo "----------------------"
@echo "Compile translation files"
@echo "----------------------"
@for pofile in `find inc/languages/ -name shaarli.po`; do \
echo "Compiling $$pofile"; \
msgfmt -v "$$pofile" -o "`dirname "$$pofile"`/`basename "$$pofile" .po`.mo"; \
done;
### Run ESLint check against Shaarli's JS files
eslint:
@yarnpkg run eslint -c .dev/.eslintrc.js assets/vintage/js/
@yarnpkg run eslint -c .dev/.eslintrc.js assets/default/js/
@yarnpkg run eslint -c .dev/.eslintrc.js assets/common/js/
### Run CSSLint check against Shaarli's SCSS files
sasslint:
@yarnpkg run stylelint --config .dev/.stylelintrc.js 'assets/default/scss/*.scss'
##
# Security scans
##
# trivy version (https://github.com/aquasecurity/trivy/releases)
TRIVY_VERSION=0.44.0
# default trivy exit code when vulnerabilities are found
TRIVY_EXIT_CODE=1
# default docker image to scan with trivy
TRIVY_TARGET_DOCKER_IMAGE=ghcr.io/shaarli/shaarli:latest
### download trivy vulneravbility scanner
download_trivy:
wget --quiet --continue -O trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz https://github.com/aquasecurity/trivy/releases/download/v$(TRIVY_VERSION)/trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz
tar -z -x trivy -f trivy_$(TRIVY_VERSION)_Linux-64bit.tar.gz
### run trivy vulnerability scanner on docker image
test_trivy_docker: download_trivy
./trivy --exit-code $(TRIVY_EXIT_CODE) image $(TRIVY_TARGET_DOCKER_IMAGE)
### run trivy vulnerability scanner on composer/yarn dependency trees
test_trivy_repo: download_trivy
./trivy --exit-code $(TRIVY_EXIT_CODE) fs composer.lock
./trivy --exit-code $(TRIVY_EXIT_CODE) fs yarn.lock

View file

@ -1,62 +0,0 @@
---
# Shaarli - Docker Compose example configuration
#
# See:
# - https://shaarli.readthedocs.io/en/master/Docker/#docker-compose
#
# Environment variables:
# - SHAARLI_VIRTUAL_HOST Fully Qualified Domain Name for the Shaarli instance
# - SHAARLI_LETSENCRYPT_EMAIL Contact email for certificate renewal
# - SHAARLI_DOCKER_TAG Shaarli docker tag to use
# See: https://github.com/shaarli/Shaarli/pkgs/container/shaarli/versions?filters%5Bversion_type%5D=tagged
version: '3'
networks:
http-proxy:
volumes:
traefik-acme:
shaarli-cache:
shaarli-data:
services:
shaarli:
image: ghcr.io/shaarli/shaarli:${SHAARLI_DOCKER_TAG}
build: ./
networks:
- http-proxy
volumes:
- shaarli-cache:/var/www/shaarli/cache
- shaarli-data:/var/www/shaarli/data
labels:
traefik.domain: "${SHAARLI_VIRTUAL_HOST}"
traefik.backend: shaarli
traefik.frontend.rule: "Host:${SHAARLI_VIRTUAL_HOST}"
traefik:
image: traefik:1.7-alpine
command:
- "--defaultentrypoints=http,https"
- "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https"
- "--entrypoints=Name:https Address::443 TLS"
- "--retry"
- "--docker"
- "--docker.domain=${SHAARLI_VIRTUAL_HOST}"
- "--docker.exposedbydefault=true"
- "--docker.watch=true"
- "--acme"
- "--acme.domains=${SHAARLI_VIRTUAL_HOST}"
- "--acme.email=${SHAARLI_LETSENCRYPT_EMAIL}"
- "--acme.entrypoint=https"
- "--acme.onhostrule=true"
- "--acme.storage=/acme/acme.json"
- "--acme.httpchallenge"
- "--acme.httpchallenge.entrypoint=http"
networks:
- http-proxy
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/acme

7486
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
colors="true">
<testsuites>
<testsuite name="unit-tests">
<directory>tests</directory>
<exclude>tests/languages</exclude>
</testsuite>
<testsuite name="language-de">
<directory>tests/languages/de</directory>
</testsuite>
<testsuite name="language-en">
<directory>tests/languages/en</directory>
</testsuite>
<testsuite name="language-fr">
<directory>tests/languages/fr</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">application</directory>
</whitelist>
</filter>
</phpunit>

View file

@ -1,13 +0,0 @@
<IfModule version_module>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
<IfVersion < 2.4>
Allow from none
Deny from all
</IfVersion>
</IfModule>
<IfModule !version_module>
Require all denied
</IfModule>

View file

@ -1,206 +0,0 @@
<?php
namespace Shaarli;
use DateTime;
use Shaarli\Bookmark\Bookmark;
class HistoryTest extends \Shaarli\TestCase
{
/**
* @var string History file path
*/
protected static $historyFilePath = 'sandbox/history.php';
/**
* Delete history file.
*/
protected function setUp(): void
{
if (file_exists(self::$historyFilePath)) {
unlink(self::$historyFilePath);
}
}
/**
* Test that the history file is created if it doesn't exist.
*/
public function testConstructLazyLoading()
{
new History(self::$historyFilePath);
$this->assertFileNotExists(self::$historyFilePath);
}
/**
* Test that the history file is created if it doesn't exist.
*/
public function testAddEventCreateFile()
{
$history = new History(self::$historyFilePath);
$history->updateSettings();
$this->assertFileExists(self::$historyFilePath);
}
/**
* Not writable history file: raise an exception.
*/
public function testConstructNotWritable()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('History file isn\'t readable or writable');
touch(self::$historyFilePath);
chmod(self::$historyFilePath, 0440);
$history = new History(self::$historyFilePath);
$history->updateSettings();
}
/**
* Not parsable history file: raise an exception.
*/
public function testConstructNotParsable()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessageRegExp('/Could not parse history file/');
file_put_contents(self::$historyFilePath, 'not parsable');
$history = new History(self::$historyFilePath);
// gzinflate generates a warning
@$history->updateSettings();
}
/**
* Test add link event
*/
public function testAddLink()
{
$history = new History(self::$historyFilePath);
$bookmark = (new Bookmark())->setId(0);
$history->addLink($bookmark);
$actual = $history->getHistory()[0];
$this->assertEquals(History::CREATED, $actual['event']);
$this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
$this->assertEquals(0, $actual['id']);
$history = new History(self::$historyFilePath);
$bookmark = (new Bookmark())->setId(1);
$history->addLink($bookmark);
$actual = $history->getHistory()[0];
$this->assertEquals(History::CREATED, $actual['event']);
$this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
$this->assertEquals(1, $actual['id']);
}
// /**
// * Test updated link event
// */
// public function testUpdateLink()
// {
// $history = new History(self::$historyFilePath);
// $history->updateLink(['id' => 1]);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::UPDATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
// }
//
// /**
// * Test delete link event
// */
// public function testDeleteLink()
// {
// $history = new History(self::$historyFilePath);
// $history->deleteLink(['id' => 1]);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::DELETED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
// }
//
// /**
// * Test updated settings event
// */
// public function testUpdateSettings()
// {
// $history = new History(self::$historyFilePath);
// $history->updateSettings();
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::SETTINGS, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEmpty($actual['id']);
// }
//
// /**
// * Make sure that new items are stored at the beginning
// */
// public function testHistoryOrder()
// {
// $history = new History(self::$historyFilePath);
// $history->updateLink(['id' => 1]);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::UPDATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
//
// $history->addLink(['id' => 1]);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::CREATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
// }
//
// /**
// * Re-read history from file after writing an event
// */
// public function testHistoryRead()
// {
// $history = new History(self::$historyFilePath);
// $history->updateLink(['id' => 1]);
// $history = new History(self::$historyFilePath);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::UPDATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
// }
//
// /**
// * Re-read history from file after writing an event and make sure that the order is correct
// */
// public function testHistoryOrderRead()
// {
// $history = new History(self::$historyFilePath);
// $history->updateLink(['id' => 1]);
// $history->addLink(['id' => 1]);
//
// $history = new History(self::$historyFilePath);
// $actual = $history->getHistory()[0];
// $this->assertEquals(History::CREATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
//
// $actual = $history->getHistory()[1];
// $this->assertEquals(History::UPDATED, $actual['event']);
// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
// $this->assertEquals(1, $actual['id']);
// }
//
// /**
// * Test retention time: delete old entries.
// */
// public function testHistoryRententionTime()
// {
// $history = new History(self::$historyFilePath, 5);
// $history->updateLink(['id' => 1]);
// $this->assertEquals(1, count($history->getHistory()));
// $arr = $history->getHistory();
// $arr[0]['datetime'] = new DateTime('-1 hour');
// FileUtils::writeFlatDB(self::$historyFilePath, $arr);
//
// $history = new History(self::$historyFilePath, 60);
// $this->assertEquals(1, count($history->getHistory()));
// $this->assertEquals(1, $history->getHistory()[0]['id']);
// $history->updateLink(['id' => 2]);
// $this->assertEquals(1, count($history->getHistory()));
// $this->assertEquals(2, $history->getHistory()[0]['id']);
// }
}

View file

@ -1,229 +0,0 @@
<?php
namespace Shaarli;
use Shaarli\Config\ConfigManager;
/**
* Class LanguagesTest.
*/
class LanguagesTest extends \Shaarli\TestCase
{
/**
* @var string Config file path (without extension).
*/
protected static $configFile = 'tests/utils/config/configJson';
/**
* @var ConfigManager
*/
protected $conf;
/**
*
*/
protected function setUp(): void
{
$this->conf = new ConfigManager(self::$configFile);
}
/**
* Test t() with a simple non identified value.
*/
public function testTranslateSingleNotIDGettext()
{
$this->conf->set('translation.mode', 'gettext');
new Languages('en', $this->conf);
$text = 'abcdé 564 fgK';
$this->assertEquals($text, t($text));
}
/**
* Test t() with a simple identified value in gettext mode.
*/
public function testTranslateSingleIDGettext()
{
$this->conf->set('translation.mode', 'gettext');
new Languages('en', $this->conf);
$text = 'permalink';
$this->assertEquals($text, t($text));
}
/**
* Test t() with a non identified plural form in gettext mode.
*/
public function testTranslatePluralNotIDGettext()
{
$this->conf->set('translation.mode', 'gettext');
new Languages('en', $this->conf);
$text = 'sandwich';
$nText = 'sandwiches';
$this->assertEquals('sandwiches', t($text, $nText, 0));
$this->assertEquals('sandwich', t($text, $nText, 1));
$this->assertEquals('sandwiches', t($text, $nText, 2));
}
/**
* Test t() with an identified plural form in gettext mode.
*/
public function testTranslatePluralIDGettext()
{
$this->conf->set('translation.mode', 'gettext');
new Languages('en', $this->conf);
$text = 'shaare';
$nText = 'shaares';
// In english, zero is followed by plural form
$this->assertEquals('shaares', t($text, $nText, 0));
$this->assertEquals('shaare', t($text, $nText, 1));
$this->assertEquals('shaares', t($text, $nText, 2));
}
/**
* Test t() with a simple non identified value.
*/
public function testTranslateSingleNotIDPhp()
{
$this->conf->set('translation.mode', 'php');
new Languages('en', $this->conf);
$text = 'abcdé 564 fgK';
$this->assertEquals($text, t($text));
}
/**
* Test t() with a simple identified value in PHP mode.
*/
public function testTranslateSingleIDPhp()
{
$this->conf->set('translation.mode', 'php');
new Languages('en', $this->conf);
$text = 'permalink';
$this->assertEquals($text, t($text));
}
/**
* Test t() with a non identified plural form in PHP mode.
*/
public function testTranslatePluralNotIDPhp()
{
$this->conf->set('translation.mode', 'php');
new Languages('en', $this->conf);
$text = 'sandwich';
$nText = 'sandwiches';
$this->assertEquals('sandwiches', t($text, $nText, 0));
$this->assertEquals('sandwich', t($text, $nText, 1));
$this->assertEquals('sandwiches', t($text, $nText, 2));
}
/**
* Test t() with an identified plural form in PHP mode.
*/
public function testTranslatePluralIDPhp()
{
$this->conf->set('translation.mode', 'php');
new Languages('en', $this->conf);
$text = 'shaare';
$nText = 'shaares';
// In english, zero is followed by plural form
$this->assertEquals('shaares', t($text, $nText, 0));
$this->assertEquals('shaare', t($text, $nText, 1));
$this->assertEquals('shaares', t($text, $nText, 2));
}
/**
* Test t() with an invalid language set in the configuration in gettext mode.
*/
public function testTranslateWithInvalidConfLanguageGettext()
{
$this->conf->set('translation.mode', 'gettext');
$this->conf->set('translation.language', 'nope');
new Languages('fr', $this->conf);
$text = 'grumble';
$this->assertEquals($text, t($text));
}
/**
* Test t() with an invalid language set in the configuration in PHP mode.
*/
public function testTranslateWithInvalidConfLanguagePhp()
{
$this->conf->set('translation.mode', 'php');
$this->conf->set('translation.language', 'nope');
new Languages('fr', $this->conf);
$text = 'grumble';
$this->assertEquals($text, t($text));
}
/**
* Test t() with an invalid language set with auto language in gettext mode.
*/
public function testTranslateWithInvalidAutoLanguageGettext()
{
$this->conf->set('translation.mode', 'gettext');
new Languages('nope', $this->conf);
$text = 'grumble';
$this->assertEquals($text, t($text));
}
/**
* Test t() with an invalid language set with auto language in PHP mode.
*/
public function testTranslateWithInvalidAutoLanguagePhp()
{
$this->conf->set('translation.mode', 'php');
new Languages('nope', $this->conf);
$text = 'grumble';
$this->assertEquals($text, t($text));
}
/**
* Test t() with an extension language file coming from the theme in gettext mode
*/
public function testTranslationThemeExtensionGettext()
{
$this->conf->set('translation.mode', 'gettext');
$this->conf->set('raintpl_tpl', 'tests/utils/customtpl/');
$this->conf->set('theme', 'dummy');
new Languages('en', $this->conf);
$txt = 'rooster'; // ignore me poedit
$this->assertEquals('rooster', t($txt, $txt, 1, 'dummy'));
}
/**
* Test t() with an extension language file coming from the theme in PHP mode
*/
public function testTranslationThemeExtensionPhp()
{
$this->conf->set('translation.mode', 'php');
$this->conf->set('raintpl_tpl', 'tests/utils/customtpl/');
$this->conf->set('theme', 'dummy');
new Languages('en', $this->conf);
$txt = 'rooster'; // ignore me poedit
$this->assertEquals('rooster', t($txt, $txt, 1, 'dummy'));
}
/**
* Test t() with an extension language file in gettext mode
*/
public function testTranslationExtensionGettext()
{
$this->conf->set('translation.mode', 'gettext');
$this->conf->set('translation.extensions.test', 'tests/utils/languages/');
new Languages('en', $this->conf);
$txt = 'car'; // ignore me poedit
$this->assertEquals('car', t($txt, $txt, 1, 'test'));
$this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
}
/**
* Test t() with an extension language file in PHP mode
*/
public function testTranslationExtensionPhp()
{
$this->conf->set('translation.mode', 'php');
$this->conf->set('translation.extensions.test', 'tests/utils/languages/');
new Languages('en', $this->conf);
$txt = 'car'; // ignore me poedit
$this->assertEquals('car', t($txt, $txt, 1, 'test'));
$this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
}
}

View file

@ -1,178 +0,0 @@
<?php
namespace Shaarli\Plugin;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
/**
* Unit tests for Plugins
*/
class PluginManagerTest extends \Shaarli\TestCase
{
/**
* Path to tests plugin.
* @var string $pluginPath
*/
private static $pluginPath = 'tests/plugins';
/**
* Test plugin.
* @var string $pluginName
*/
private static $pluginName = 'test';
/**
* @var PluginManager $pluginManager Plugin Mananger instance.
*/
protected $pluginManager;
public function setUp(): void
{
$conf = new ConfigManager('');
$this->pluginManager = new PluginManager($conf);
}
/**
* Test plugin loading and hook execution.
*/
public function testPlugin(): void
{
PluginManager::$PLUGINS_PATH = self::$pluginPath;
$this->pluginManager->load([self::$pluginName]);
$this->assertTrue(function_exists('hook_test_random'));
$data = [0 => 'woot'];
$this->pluginManager->executeHooks('random', $data);
static::assertCount(2, $data);
static::assertSame('woot', $data[1]);
$data = [0 => 'woot'];
$this->pluginManager->executeHooks('random', $data, ['target' => 'test']);
static::assertCount(2, $data);
static::assertSame('page test', $data[1]);
$data = [0 => 'woot'];
$this->pluginManager->executeHooks('random', $data, ['loggedin' => true]);
static::assertCount(2, $data);
static::assertEquals('loggedin', $data[1]);
$data = [0 => 'woot'];
$this->pluginManager->executeHooks('random', $data, ['loggedin' => null]);
static::assertCount(3, $data);
static::assertEquals('loggedin', $data[1]);
static::assertArrayHasKey(2, $data);
static::assertNull($data[2]);
}
/**
* Test plugin loading and hook execution with an error: raise an incompatibility error.
*/
public function testPluginWithPhpError(): void
{
PluginManager::$PLUGINS_PATH = self::$pluginPath;
$this->pluginManager->load([self::$pluginName]);
$this->assertTrue(function_exists('hook_test_error'));
$data = [];
$this->pluginManager->executeHooks('error', $data);
$this->assertRegExp(
'/test \[plugin incompatibility\]: Class [\'"]Unknown[\'"] not found/',
$this->pluginManager->getErrors()[0]
);
}
/**
* Test missing plugin loading.
*/
public function testPluginNotFound(): void
{
$this->pluginManager->load([]);
$this->pluginManager->load(['nope', 'renope']);
$this->addToAssertionCount(1);
}
/**
* Test plugin metadata loading.
*/
public function testGetPluginsMeta(): void
{
PluginManager::$PLUGINS_PATH = self::$pluginPath;
$this->pluginManager->load([self::$pluginName]);
$expectedParameters = [
'pop' => [
'value' => '',
'desc' => 'pop description',
],
'hip' => [
'value' => '',
'desc' => '',
],
];
$meta = $this->pluginManager->getPluginsMeta();
$this->assertEquals('test plugin', $meta[self::$pluginName]['description']);
$this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']);
}
/**
* Test plugin custom routes - note that there is no check on callable functions
*/
public function testRegisteredRoutes(): void
{
PluginManager::$PLUGINS_PATH = self::$pluginPath;
$this->pluginManager->load([self::$pluginName]);
$expectedParameters = [
[
'method' => 'GET',
'route' => '/test',
'callable' => 'getFunction',
],
[
'method' => 'POST',
'route' => '/custom',
'callable' => 'postFunction',
],
];
$meta = $this->pluginManager->getRegisteredRoutes();
static::assertSame($expectedParameters, $meta[self::$pluginName]);
}
/**
* Test plugin custom routes with invalid route
*/
public function testRegisteredRoutesInvalid(): void
{
$plugin = 'test_route_invalid';
$this->pluginManager->load([$plugin]);
$meta = $this->pluginManager->getRegisteredRoutes();
static::assertSame([], $meta);
$errors = $this->pluginManager->getErrors();
static::assertSame(['test_route_invalid [plugin incompatibility]: trying to register invalid route.'], $errors);
}
public function testSearchFilterPlugin(): void
{
PluginManager::$PLUGINS_PATH = self::$pluginPath;
$this->pluginManager->load([self::$pluginName]);
static::assertNull($this->pluginManager->getFilterSearchEntryHooks());
static::assertTrue($this->pluginManager->filterSearchEntry(new Bookmark(), ['_result' => true]));
static::assertCount(1, $this->pluginManager->getFilterSearchEntryHooks());
static::assertSame('hook_test_filter_search_entry', $this->pluginManager->getFilterSearchEntryHooks()[0]);
static::assertFalse($this->pluginManager->filterSearchEntry(new Bookmark(), ['_result' => false]));
}
}

View file

@ -1,77 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli;
/**
* Helper class extending \PHPUnit\Framework\TestCase.
* Used to make Shaarli UT run on multiple versions of PHPUnit.
*/
class TestCase extends \PHPUnit\Framework\TestCase
{
/**
* expectExceptionMessageRegExp has been removed and replaced by expectExceptionMessageMatches in PHPUnit 9.
*/
public function expectExceptionMessageRegExp(string $regularExpression): void
{
if (method_exists($this, 'expectExceptionMessageMatches')) {
$this->expectExceptionMessageMatches($regularExpression);
} else {
parent::expectExceptionMessageRegExp($regularExpression);
}
}
/**
* assertContains is now used for iterable, strings should use assertStringContainsString
*/
public function assertContainsPolyfill($expected, $actual, string $message = ''): void
{
if (is_string($actual) && method_exists($this, 'assertStringContainsString')) {
static::assertStringContainsString($expected, $actual, $message);
} else {
static::assertContains($expected, $actual, $message);
}
}
/**
* assertNotContains is now used for iterable, strings should use assertStringNotContainsString
*/
public function assertNotContainsPolyfill($expected, $actual, string $message = ''): void
{
if (is_string($actual) && method_exists($this, 'assertStringNotContainsString')) {
static::assertStringNotContainsString($expected, $actual, $message);
} else {
static::assertNotContains($expected, $actual, $message);
}
}
/**
* assertFileNotExists has been renamed in assertFileDoesNotExist
*/
public static function assertFileNotExists(string $filename, string $message = ''): void
{
if (method_exists(TestCase::class, 'assertFileDoesNotExist')) {
static::assertFileDoesNotExist($filename, $message);
} else {
parent::assertFileNotExists($filename, $message);
}
}
/**
* assertRegExp has been renamed in assertMatchesRegularExpression
*/
public static function assertRegExp(string $pattern, string $string, string $message = ''): void
{
if (method_exists(TestCase::class, 'assertMatchesRegularExpression')) {
static::assertMatchesRegularExpression($pattern, $string, $message);
} else {
parent::assertRegExp($pattern, $string, $message);
}
}
public function isInTestsContext(): bool
{
return true;
}
}

View file

@ -1,115 +0,0 @@
<?php
namespace Shaarli;
use Shaarli\Config\ConfigManager;
use WebThumbnailer\Application\ConfigManager as WTConfigManager;
/**
* Class ThumbnailerTest
*
* We only make 1 thumb test because:
*
* 1. the thumbnailer library is itself tested
* 2. we don't want to make too many external requests during the tests
*/
class ThumbnailerTest extends TestCase
{
protected const WIDTH = 190;
protected const HEIGHT = 210;
/**
* @var Thumbnailer;
*/
protected $thumbnailer;
/**
* @var ConfigManager
*/
protected $conf;
protected function setUp(): void
{
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL);
$this->conf->set('thumbnails.width', self::WIDTH);
$this->conf->set('thumbnails.height', self::HEIGHT);
$this->conf->set('dev.debug', true);
$this->thumbnailer = new Thumbnailer($this->conf);
// cache files in the sandbox
WTConfigManager::addFile('tests/utils/config/wt.json');
}
protected function tearDown(): void
{
$this->rrmdirContent('sandbox/');
}
/**
* Test a thumbnail with a custom size in 'all' mode.
*/
public function testThumbnailAllValid()
{
$thumb = $this->thumbnailer->get('https://gitlab.com/shaarli/Shaarli');
$this->assertNotFalse($thumb);
$image = imagecreatefromstring(file_get_contents($thumb));
$this->assertEquals(self::WIDTH, imagesx($image));
$this->assertEquals(self::HEIGHT, imagesy($image));
}
/**
* Test a thumbnail with a custom size in 'common' mode.
*/
public function testThumbnailCommonValid()
{
$this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
$thumb = $this->thumbnailer->get('https://imgur.com/jlFgGpe');
$this->assertNotFalse($thumb);
$image = imagecreatefromstring(file_get_contents($thumb));
$this->assertEquals(self::WIDTH, imagesx($image));
$this->assertEquals(self::HEIGHT, imagesy($image));
}
/**
* Test a thumbnail in 'common' mode which isn't include in common websites.
*/
public function testThumbnailCommonInvalid()
{
$this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
$thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/');
$this->assertFalse($thumb);
}
/**
* Test a thumbnail that can't be retrieved.
*/
public function testThumbnailNotValid()
{
$oldlog = ini_get('error_log');
ini_set('error_log', '/dev/null');
$thumbnailer = new Thumbnailer(new ConfigManager());
$thumb = $thumbnailer->get('nope');
$this->assertFalse($thumb);
ini_set('error_log', $oldlog);
}
protected function rrmdirContent($dir)
{
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (is_dir($dir . "/" . $object)) {
$this->rrmdirContent($dir . "/" . $object);
} else {
unlink($dir . "/" . $object);
}
}
}
}
}
}

View file

@ -1,104 +0,0 @@
<?php
/**
* TimeZone's tests
*/
namespace Shaarli\Tests;
use Shaarli\TestCase;
/**
* Unitary tests for timezone utilities
*/
class TimeZoneTest extends TestCase
{
/**
* @var array of timezones
*/
protected $installedTimezones;
protected function setUp(): void
{
$this->installedTimezones = [
'Antarctica/Syowa',
'Europe/London',
'Europe/Paris',
'UTC'
];
}
/**
* Generate a timezone selection form
*/
public function testGenerateTimeZoneForm()
{
$expected = [
'continents' => [
'Antarctica',
'Europe',
'UTC',
'selected' => '',
],
'cities' => [
['continent' => 'Antarctica', 'city' => 'Syowa'],
['continent' => 'Europe', 'city' => 'London'],
['continent' => 'Europe', 'city' => 'Paris'],
['continent' => 'UTC', 'city' => 'UTC'],
'selected' => '',
]
];
list($continents, $cities) = generateTimeZoneData($this->installedTimezones);
$this->assertEquals($expected['continents'], $continents);
$this->assertEquals($expected['cities'], $cities);
}
/**
* Generate a timezone selection form, with a preselected timezone
*/
public function testGenerateTimeZoneFormPreselected()
{
$expected = [
'continents' => [
'Antarctica',
'Europe',
'UTC',
'selected' => 'Antarctica',
],
'cities' => [
['continent' => 'Antarctica', 'city' => 'Syowa'],
['continent' => 'Europe', 'city' => 'London'],
['continent' => 'Europe', 'city' => 'Paris'],
['continent' => 'UTC', 'city' => 'UTC'],
'selected' => 'Syowa',
]
];
list($continents, $cities) = generateTimeZoneData($this->installedTimezones, 'Antarctica/Syowa');
$this->assertEquals($expected['continents'], $continents);
$this->assertEquals($expected['cities'], $cities);
}
/**
* Check valid timezones
*/
public function testValidTimeZone()
{
$this->assertTrue(isTimeZoneValid('America', 'Argentina/Ushuaia'));
$this->assertTrue(isTimeZoneValid('Europe', 'Oslo'));
}
/**
* Check invalid timezones
*/
public function testInvalidTimeZone()
{
$this->assertFalse(isTimeZoneValid('CEST', 'CEST'));
$this->assertFalse(isTimeZoneValid('Europe', 'Atlantis'));
$this->assertFalse(isTimeZoneValid('Middle_Earth', 'Moria'));
$this->assertFalse(isTimeZoneValid('UTC', 'UTC'));
}
}

View file

@ -1,463 +0,0 @@
<?php
/**
* Utilities' tests
*/
namespace Shaarli\Tests;
use DateTime;
use Shaarli\TestCase;
/**
* Unitary tests for Shaarli utilities
*/
class UtilsTest extends TestCase
{
// Log file
protected static $testLogFile = 'tests.log';
// Expected log date format
protected static $dateFormat = 'Y/m/d H:i:s';
/**
* @var string Save the current timezone.
*/
protected static $defaultTimeZone;
/**
* Assign reference data
*/
public static function setUpBeforeClass(): void
{
self::$defaultTimeZone = date_default_timezone_get();
// Timezone without DST for test consistency
date_default_timezone_set('Africa/Nairobi');
}
/**
* Reset the timezone
*/
public static function tearDownAfterClass(): void
{
date_default_timezone_set(self::$defaultTimeZone);
}
/**
* Resets test data before each test
*/
protected function setUp(): void
{
if (file_exists(self::$testLogFile)) {
unlink(self::$testLogFile);
}
}
/**
* Returns a list of the elements from the last logged entry
*
* @return array (date, ip address, message)
*/
protected function getLastLogEntry()
{
$logFile = file(self::$testLogFile);
return explode(' - ', trim(array_pop($logFile), PHP_EOL));
}
/**
* Format a log a message - IPv4 client address
*/
public function testFormatLogIp4()
{
$message = 'IPv4 client connected';
$log = format_log($message, '127.0.0.1');
static::assertSame('- 127.0.0.1 - IPv4 client connected', $log);
}
/**
* Format a log a message - IPv6 client address
*/
public function testFormatLogIp6()
{
$message = 'IPv6 client connected';
$log = format_log($message, '2001:db8::ff00:42:8329');
static::assertSame('- 2001:db8::ff00:42:8329 - IPv6 client connected', $log);
}
/**
* Represent a link by its hash
*/
public function testSmallHash()
{
$this->assertEquals('CyAAJw', smallHash('http://test.io'));
$this->assertEquals(6, strlen(smallHash('https://github.com')));
}
/**
* Look for a substring at the beginning of a string
*/
public function testStartsWithCaseInsensitive()
{
$this->assertTrue(startsWith('Lorem ipsum', 'lorem', false));
$this->assertTrue(startsWith('Lorem ipsum', 'LoReM i', false));
}
/**
* Look for a substring at the beginning of a string (case-sensitive)
*/
public function testStartsWithCaseSensitive()
{
$this->assertTrue(startsWith('Lorem ipsum', 'Lorem', true));
$this->assertFalse(startsWith('Lorem ipsum', 'lorem', true));
$this->assertFalse(startsWith('Lorem ipsum', 'LoReM i', true));
}
/**
* Look for a substring at the beginning of a string (Unicode)
*/
public function testStartsWithSpecialChars()
{
$this->assertTrue(startsWith('å!ùµ', 'å!', false));
$this->assertTrue(startsWith('µ$åù', 'µ$', true));
}
/**
* Look for a substring at the end of a string
*/
public function testEndsWithCaseInsensitive()
{
$this->assertTrue(endsWith('Lorem ipsum', 'ipsum', false));
$this->assertTrue(endsWith('Lorem ipsum', 'm IpsUM', false));
}
/**
* Look for a substring at the end of a string (case-sensitive)
*/
public function testEndsWithCaseSensitive()
{
$this->assertTrue(endsWith('lorem Ipsum', 'Ipsum', true));
$this->assertFalse(endsWith('lorem Ipsum', 'ipsum', true));
$this->assertFalse(endsWith('lorem Ipsum', 'M IPsuM', true));
}
/**
* Look for a substring at the end of a string (Unicode)
*/
public function testEndsWithSpecialChars()
{
$this->assertTrue(endsWith('å!ùµ', 'ùµ', false));
$this->assertTrue(endsWith('µ$åù', 'åù', true));
}
/**
* Check valid date strings, according to a DateTime format
*/
public function testCheckValidDateFormat()
{
$this->assertTrue(checkDateFormat('Ymd', '20150627'));
$this->assertTrue(checkDateFormat('Y-m-d', '2015-06-27'));
}
/**
* Check erroneous date strings, according to a DateTime format
*/
public function testCheckInvalidDateFormat()
{
$this->assertFalse(checkDateFormat('Ymd', '2015'));
$this->assertFalse(checkDateFormat('Y-m-d', '2015-06'));
$this->assertFalse(checkDateFormat('Ymd', 'DeLorean'));
}
/**
* Test generate location with valid data.
*/
public function testGenerateLocation()
{
$ref = 'http://localhost/?test';
$this->assertEquals($ref, generateLocation($ref, 'localhost'));
$ref = 'http://localhost:8080/?test';
$this->assertEquals($ref, generateLocation($ref, 'localhost:8080'));
$ref = '?localreferer#hash';
$this->assertEquals($ref, generateLocation($ref, 'localhost:8080'));
}
/**
* Test generate location - anti loop.
*/
public function testGenerateLocationLoop()
{
$ref = 'http://localhost/?test';
$this->assertEquals('./?', generateLocation($ref, 'localhost', ['test']));
}
/**
* Test generate location - from other domain.
*/
public function testGenerateLocationOut()
{
$ref = 'http://somewebsite.com/?test';
$this->assertEquals('./?', generateLocation($ref, 'localhost'));
}
/**
* Test generateSecretApi.
*/
public function testGenerateSecretApi()
{
$this->assertEquals(12, strlen(generate_api_secret('foo', 'bar')));
}
/**
* Test generateSecretApi with invalid parameters.
*/
public function testGenerateSecretApiInvalid()
{
$this->assertFalse(generate_api_secret('', ''));
$this->assertFalse(generate_api_secret(false, false));
}
/**
* Test normalize_spaces.
*/
public function testNormalizeSpace()
{
$str = ' foo bar is important ';
$this->assertEquals('foo bar is important', normalize_spaces($str));
$this->assertEquals('foo', normalize_spaces('foo'));
$this->assertEquals('', normalize_spaces(''));
$this->assertEquals(null, normalize_spaces(null));
}
/**
* Test arrays_combine
*/
public function testCartesianProductGenerator()
{
$arr = [['ab', 'cd'], ['ef', 'gh'], ['ij', 'kl'], ['m']];
$expected = [
['ab', 'ef', 'ij', 'm'],
['ab', 'ef', 'kl', 'm'],
['ab', 'gh', 'ij', 'm'],
['ab', 'gh', 'kl', 'm'],
['cd', 'ef', 'ij', 'm'],
['cd', 'ef', 'kl', 'm'],
['cd', 'gh', 'ij', 'm'],
['cd', 'gh', 'kl', 'm'],
];
$this->assertEquals($expected, iterator_to_array(cartesian_product_generator($arr)));
}
/**
* Test date_format() with invalid parameter.
*/
public function testDateFormatInvalid()
{
$this->assertFalse(format_date([]));
$this->assertFalse(format_date(null));
}
/**
* Test is_integer_mixed with valid values
*/
public function testIsIntegerMixedValid()
{
$this->assertTrue(is_integer_mixed(12));
$this->assertTrue(is_integer_mixed('12'));
$this->assertTrue(is_integer_mixed(-12));
$this->assertTrue(is_integer_mixed('-12'));
$this->assertTrue(is_integer_mixed(0));
$this->assertTrue(is_integer_mixed('0'));
$this->assertTrue(is_integer_mixed(0x0a));
}
/**
* Test is_integer_mixed with invalid values
*/
public function testIsIntegerMixedInvalid()
{
$this->assertFalse(is_integer_mixed(true));
$this->assertFalse(is_integer_mixed(false));
$this->assertFalse(is_integer_mixed([]));
$this->assertFalse(is_integer_mixed(['test']));
$this->assertFalse(is_integer_mixed([12]));
$this->assertFalse(is_integer_mixed(new DateTime()));
$this->assertFalse(is_integer_mixed('0x0a'));
$this->assertFalse(is_integer_mixed('12k'));
$this->assertFalse(is_integer_mixed('k12'));
$this->assertFalse(is_integer_mixed(''));
}
/**
* Test return_bytes
*/
public function testReturnBytes()
{
$this->assertEquals(2 * 1024, return_bytes('2k'));
$this->assertEquals(2 * 1024, return_bytes('2K'));
$this->assertEquals(2 * (pow(1024, 2)), return_bytes('2m'));
$this->assertEquals(2 * (pow(1024, 2)), return_bytes('2M'));
$this->assertEquals(2 * (pow(1024, 3)), return_bytes('2g'));
$this->assertEquals(2 * (pow(1024, 3)), return_bytes('2G'));
$this->assertEquals(374, return_bytes('374'));
$this->assertEquals(374, return_bytes(374));
$this->assertEquals(0, return_bytes('0'));
$this->assertEquals(0, return_bytes(0));
$this->assertEquals(-1, return_bytes('-1'));
$this->assertEquals(-1, return_bytes(-1));
$this->assertEquals('', return_bytes(''));
}
/**
* Test human_bytes
*/
public function testHumanBytes()
{
$this->assertEquals('2' . t('kiB'), human_bytes(2 * 1024));
$this->assertEquals('2' . t('kiB'), human_bytes(strval(2 * 1024)));
$this->assertEquals('2' . t('MiB'), human_bytes(2 * (pow(1024, 2))));
$this->assertEquals('2' . t('MiB'), human_bytes(strval(2 * (pow(1024, 2)))));
$this->assertEquals('2' . t('GiB'), human_bytes(2 * (pow(1024, 3))));
$this->assertEquals('2' . t('GiB'), human_bytes(strval(2 * (pow(1024, 3)))));
$this->assertEquals('374' . t('B'), human_bytes(374));
$this->assertEquals('374' . t('B'), human_bytes('374'));
$this->assertEquals('232' . t('kiB'), human_bytes(237481));
$this->assertEquals(t('Unlimited'), human_bytes('0'));
$this->assertEquals(t('Unlimited'), human_bytes(0));
$this->assertEquals(t('Setting not set'), human_bytes(''));
}
/**
* Test get_max_upload_size with formatting
*/
public function testGetMaxUploadSize()
{
$this->assertEquals('1' . t('MiB'), get_max_upload_size(2097152, '1024k'));
$this->assertEquals('1' . t('MiB'), get_max_upload_size('1m', '2m'));
$this->assertEquals('100' . t('B'), get_max_upload_size(100, 100));
}
/**
* Test get_max_upload_size without formatting
*/
public function testGetMaxUploadSizeRaw()
{
$this->assertEquals('1048576', get_max_upload_size(2097152, '1024k', false));
$this->assertEquals('1048576', get_max_upload_size('1m', '2m', false));
$this->assertEquals('100', get_max_upload_size(100, 100, false));
}
/**
* Test alphabetical_sort by value, not reversed, with php-intl.
*/
public function testAlphabeticalSortByValue()
{
$arr = [
'zZz',
'éee',
'éae',
'eee',
'A',
'a',
'zzz',
];
$expected = [
'a',
'A',
'éae',
'eee',
'éee',
'zzz',
'zZz',
];
alphabetical_sort($arr);
$this->assertEquals($expected, $arr);
}
/**
* Test alphabetical_sort by value, reversed, with php-intl.
*/
public function testAlphabeticalSortByValueReversed()
{
$arr = [
'zZz',
'éee',
'éae',
'eee',
'A',
'a',
'zzz',
];
$expected = [
'zZz',
'zzz',
'éee',
'eee',
'éae',
'A',
'a',
];
alphabetical_sort($arr, true);
$this->assertEquals($expected, $arr);
}
/**
* Test alphabetical_sort by keys, not reversed, with php-intl.
*/
public function testAlphabeticalSortByKeys()
{
$arr = [
'zZz' => true,
'éee' => true,
'éae' => true,
'eee' => true,
'A' => true,
'a' => true,
'zzz' => true,
];
$expected = [
'a' => true,
'A' => true,
'éae' => true,
'eee' => true,
'éee' => true,
'zzz' => true,
'zZz' => true,
];
alphabetical_sort($arr, true, true);
$this->assertEquals($expected, $arr);
}
/**
* Test alphabetical_sort by keys, reversed, with php-intl.
*/
public function testAlphabeticalSortByKeysReversed()
{
$arr = [
'zZz' => true,
'éee' => true,
'éae' => true,
'eee' => true,
'A' => true,
'a' => true,
'zzz' => true,
];
$expected = [
'zZz' => true,
'zzz' => true,
'éee' => true,
'eee' => true,
'éae' => true,
'A' => true,
'a' => true,
];
alphabetical_sort($arr, true, true);
$this->assertEquals($expected, $arr);
}
}

View file

@ -1,262 +0,0 @@
<?php
namespace Shaarli\Api;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class ApiMiddlewareTest
*
* Test the REST API Slim Middleware.
*
* Note that we can't test a valid use case here, because the middleware
* needs to call a valid controller/action during its execution.
*
* @package Api
*/
class ApiMiddlewareTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('api.secret', 'NapoleonWasALizard');
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['history'] = $history;
$this->container['pluginManager'] = new PluginManager($this->conf);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Invoke the middleware with a valid token
*/
public function testInvokeMiddlewareWithValidToken(): void
{
$next = function (Request $request, Response $response): Response {
return $response;
};
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'HTTP_AUTHORIZATION' => 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard'),
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, $next);
$this->assertEquals(200, $response->getStatusCode());
}
/**
* Invoke the middleware with a valid token
* Using specific Apache CGI redirected authorization.
*/
public function testInvokeMiddlewareWithValidTokenFromRedirectedHeader(): void
{
$next = function (Request $request, Response $response): Response {
return $response;
};
$token = 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard');
$this->container->environment['REDIRECT_HTTP_AUTHORIZATION'] = $token;
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, $next);
$this->assertEquals(200, $response->getStatusCode());
}
/**
* Invoke the middleware with the API disabled:
* should return a 401 error Unauthorized.
*/
public function testInvokeMiddlewareApiDisabled()
{
$this->conf->set('api.enabled', false);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized', $body);
}
/**
* Invoke the middleware with the API disabled in debug mode:
* should return a 401 error Unauthorized - with a specific message and a stacktrace.
*/
public function testInvokeMiddlewareApiDisabledDebug()
{
$this->conf->set('api.enabled', false);
$this->conf->set('dev.debug', true);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: API is disabled', $body->message);
$this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
}
/**
* Invoke the middleware without a token (debug):
* should return a 401 error Unauthorized - with a specific message and a stacktrace.
*/
public function testInvokeMiddlewareNoTokenProvidedDebug()
{
$this->conf->set('dev.debug', true);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: JWT token not provided', $body->message);
$this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
}
/**
* Invoke the middleware without a secret set in settings (debug):
* should return a 401 error Unauthorized - with a specific message and a stacktrace.
*/
public function testInvokeMiddlewareNoSecretSetDebug()
{
$this->conf->set('dev.debug', true);
$this->conf->set('api.secret', '');
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'HTTP_AUTHORIZATION' => 'Bearer jwt',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message);
$this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
}
/**
* Invoke the middleware with an invalid JWT token header
*/
public function testInvalidJwtAuthHeaderDebug()
{
$this->conf->set('dev.debug', true);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'HTTP_AUTHORIZATION' => 'PolarBearer jwt',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: Invalid JWT header', $body->message);
$this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
}
/**
* Invoke the middleware with an invalid JWT token (debug):
* should return a 401 error Unauthorized - with a specific message and a stacktrace.
*
* Note: specific JWT errors tests are handled in ApiUtilsTest.
*/
public function testInvokeMiddlewareInvalidJwtDebug()
{
$this->conf->set('dev.debug', true);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'HTTP_AUTHORIZATION' => 'Bearer jwt',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: Malformed JWT token', $body->message);
$this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
}
}

View file

@ -1,354 +0,0 @@
<?php
namespace Shaarli\Api;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Http\Base64Url;
/**
* Class ApiUtilsTest
*/
class ApiUtilsTest extends \Shaarli\TestCase
{
/**
* Force the timezone for ISO datetimes.
*/
public static function setUpBeforeClass(): void
{
date_default_timezone_set('UTC');
}
/**
* Generate a valid JWT token.
*
* @param string $secret API secret used to generate the signature.
*
* @return string Generated token.
*/
public static function generateValidJwtToken($secret)
{
$header = Base64Url::encode('{
"typ": "JWT",
"alg": "HS512"
}');
$payload = Base64Url::encode('{
"iat": ' . time() . '
}');
$signature = Base64Url::encode(hash_hmac('sha512', $header . '.' . $payload, $secret, true));
return $header . '.' . $payload . '.' . $signature;
}
/**
* Generate a JWT token from given header and payload.
*
* @param string $header Header in JSON format.
* @param string $payload Payload in JSON format.
* @param string $secret API secret used to hash the signature.
*
* @return string JWT token.
*/
public static function generateCustomJwtToken($header, $payload, $secret)
{
$header = Base64Url::encode($header);
$payload = Base64Url::encode($payload);
$signature = Base64Url::encode(hash_hmac('sha512', $header . '.' . $payload, $secret, true));
return $header . '.' . $payload . '.' . $signature;
}
/**
* Test validateJwtToken() with a valid JWT token.
*/
public function testValidateJwtTokenValid()
{
$secret = 'WarIsPeace';
$this->assertTrue(ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret));
}
/**
* Test validateJwtToken() with a malformed JWT token.
*/
public function testValidateJwtTokenMalformed()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Malformed JWT token');
$token = 'ABC.DEF';
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with an empty JWT token.
*/
public function testValidateJwtTokenMalformedEmpty()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Malformed JWT token');
$token = false;
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with a JWT token without header.
*/
public function testValidateJwtTokenMalformedEmptyHeader()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Malformed JWT token');
$token = '.payload.signature';
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with a JWT token without payload
*/
public function testValidateJwtTokenMalformedEmptyPayload()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Malformed JWT token');
$token = 'header..signature';
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with a JWT token with an empty signature.
*/
public function testValidateJwtTokenInvalidSignatureEmpty()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT signature');
$token = 'header.payload.';
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with a JWT token with an invalid signature.
*/
public function testValidateJwtTokenInvalidSignature()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT signature');
$token = 'header.payload.nope';
ApiUtils::validateJwtToken($token, 'foo');
}
/**
* Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret.
*/
public function testValidateJwtTokenInvalidSignatureSecret()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT signature');
ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar');
}
/**
* Test validateJwtToken() with a JWT token with a an invalid header (not JSON).
*/
public function testValidateJwtTokenInvalidHeader()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT header');
$token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret');
ApiUtils::validateJwtToken($token, 'secret');
}
/**
* Test validateJwtToken() with a JWT token with a an invalid payload (not JSON).
*/
public function testValidateJwtTokenInvalidPayload()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT payload');
$token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret');
ApiUtils::validateJwtToken($token, 'secret');
}
/**
* Test validateJwtToken() with a JWT token without issued time.
*/
public function testValidateJwtTokenInvalidTimeEmpty()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT issued time');
$token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret');
ApiUtils::validateJwtToken($token, 'secret');
}
/**
* Test validateJwtToken() with an expired JWT token.
*/
public function testValidateJwtTokenInvalidTimeExpired()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT issued time');
$token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret');
ApiUtils::validateJwtToken($token, 'secret');
}
/**
* Test validateJwtToken() with a JWT token issued in the future.
*/
public function testValidateJwtTokenInvalidTimeFuture()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
$this->expectExceptionMessage('Invalid JWT issued time');
$token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret');
ApiUtils::validateJwtToken($token, 'secret');
}
/**
* Test formatLink() with a link using all useful fields.
*/
public function testFormatLinkComplete()
{
$indexUrl = 'https://domain.tld/sub/';
$data = [
'id' => 12,
'url' => 'http://lol.lol',
'shorturl' => 'abc',
'title' => 'Important Title',
'description' => 'It is very lol<tag>' . PHP_EOL . 'new line',
'tags' => 'blip .blop ',
'private' => '1',
'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'),
];
$bookmark = new Bookmark();
$bookmark->fromArray($data);
$expected = [
'id' => 12,
'url' => 'http://lol.lol',
'shorturl' => 'abc',
'title' => 'Important Title',
'description' => 'It is very lol<tag>' . PHP_EOL . 'new line',
'tags' => ['blip', '.blop'],
'private' => true,
'created' => '2017-01-07T16:01:02+00:00',
'updated' => '2017-01-07T16:06:12+00:00',
];
$this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl));
}
/**
* Test formatLink() with only minimal fields filled, and internal link.
*/
public function testFormatLinkMinimalNote()
{
$indexUrl = 'https://domain.tld/sub/';
$data = [
'id' => 12,
'url' => '?abc',
'shorturl' => 'abc',
'title' => 'Note',
'description' => '',
'tags' => '',
'private' => '',
'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
];
$bookmark = new Bookmark();
$bookmark->fromArray($data);
$expected = [
'id' => 12,
'url' => 'https://domain.tld/sub/?abc',
'shorturl' => 'abc',
'title' => 'Note',
'description' => '',
'tags' => [],
'private' => false,
'created' => '2017-01-07T16:01:02+00:00',
'updated' => '',
];
$this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl));
}
/**
* Test updateLink with valid data, and also unnecessary fields.
*/
public function testUpdateLink()
{
$created = \DateTime::createFromFormat('Ymd_His', '20170107_160102');
$data = [
'id' => 12,
'url' => '?abc',
'shorturl' => 'abc',
'title' => 'Note',
'description' => '',
'tags' => '',
'private' => '',
'created' => $created,
];
$old = new Bookmark();
$old->fromArray($data);
$data = [
'id' => 13,
'shorturl' => 'nope',
'url' => 'http://somewhere.else',
'title' => 'Le Cid',
'description' => 'Percé jusques au fond du cœur [...]',
'tags' => 'corneille rodrigue',
'private' => true,
'created' => 'creation',
'updated' => 'updation',
];
$new = new Bookmark();
$new->fromArray($data);
$result = ApiUtils::updateLink($old, $new);
$this->assertEquals(12, $result->getId());
$this->assertEquals('http://somewhere.else', $result->getUrl());
$this->assertEquals('abc', $result->getShortUrl());
$this->assertEquals('Le Cid', $result->getTitle());
$this->assertEquals('Percé jusques au fond du cœur [...]', $result->getDescription());
$this->assertEquals('corneille rodrigue', $result->getTagsString());
$this->assertEquals(true, $result->isPrivate());
$this->assertEquals($created, $result->getCreated());
}
/**
* Test updateLink with minimal data.
*/
public function testUpdateLinkMinimal()
{
$created = \DateTime::createFromFormat('Ymd_His', '20170107_160102');
$data = [
'id' => 12,
'url' => '?abc',
'shorturl' => 'abc',
'title' => 'Note',
'description' => 'Interesting description!',
'tags' => 'doggo',
'private' => true,
'created' => $created,
];
$old = new Bookmark();
$old->fromArray($data);
$new = new Bookmark();
$result = ApiUtils::updateLink($old, $new);
$this->assertEquals(12, $result->getId());
$this->assertEquals('', $result->getUrl());
$this->assertEquals('abc', $result->getShortUrl());
$this->assertEquals('', $result->getTitle());
$this->assertEquals('', $result->getDescription());
$this->assertEquals('', $result->getTagsString());
$this->assertEquals(false, $result->isPrivate());
$this->assertEquals($created, $result->getCreated());
}
}

View file

@ -1,215 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceHistory;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
class HistoryTest extends TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceHistory instance.
*/
protected $refHistory = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var HistoryController controller instance.
*/
protected $controller;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->refHistory = new ReferenceHistory();
$this->refHistory->write(self::$testHistory);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = true;
$this->container['history'] = new History(self::$testHistory);
$this->controller = new HistoryController($this->container);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testHistory);
}
/**
* Test /history service without parameter.
*/
public function testGetHistory()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refHistory->count(), count($data));
$this->assertEquals(History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
$this->assertEquals(History::SETTINGS, $data[1]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
$data[1]['datetime']
);
$this->assertNull($data[1]['id']);
$this->assertEquals(History::UPDATED, $data[2]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM),
$data[2]['datetime']
);
$this->assertEquals(123, $data[2]['id']);
$this->assertEquals(History::CREATED, $data[3]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM),
$data[3]['datetime']
);
$this->assertEquals(124, $data[3]['id']);
$this->assertEquals(History::CREATED, $data[4]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
$data[4]['datetime']
);
$this->assertEquals(123, $data[4]['id']);
}
/**
* Test /history service with limit parameter.
*/
public function testGetHistoryLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
}
/**
* Test /history service with offset parameter.
*/
public function testGetHistoryOffset()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=4'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(History::CREATED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(123, $data[0]['id']);
}
/**
* Test /history service with since parameter.
*/
public function testGetHistorySince()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'since=2017-03-03T00:00:00%2B00:00'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
}
/**
* Test /history service with since parameter.
*/
public function testGetHistorySinceOffsetLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'since=2017-02-01T00:00:00%2B00:00&offset=1&limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(History::SETTINGS, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
$data[0]['datetime']
);
}
}

View file

@ -1,131 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class InfoTest
*
* Test REST API controller Info.
*
* @package Api\Controllers
*/
class InfoTest extends TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Info controller instance.
*/
protected $controller;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$this->pluginManager = new PluginManager($this->conf);
$history = new History('sandbox/history.php');
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$history,
$mutex,
true
);
$this->container['history'] = null;
$this->controller = new Info($this->container);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Test /info service.
*/
public function testGetInfo()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getInfo($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']);
$this->assertEquals(2, $data['private_counter']);
$this->assertEquals('Shaarli', $data['settings']['title']);
$this->assertEquals('?', $data['settings']['header_link']);
$this->assertEquals('Europe/Paris', $data['settings']['timezone']);
$this->assertEquals(ConfigManager::$DEFAULT_PLUGINS, $data['settings']['enabled_plugins']);
$this->assertEquals(true, $data['settings']['default_private_links']);
$title = 'My bookmarks';
$headerLink = 'http://shaarli.tld';
$timezone = 'Europe/Paris';
$enabledPlugins = ['foo', 'bar'];
$defaultPrivateLinks = true;
$this->conf->set('general.title', $title);
$this->conf->set('general.header_link', $headerLink);
$this->conf->set('general.timezone', $timezone);
$this->conf->set('general.enabled_plugins', $enabledPlugins);
$this->conf->set('privacy.default_private_links', $defaultPrivateLinks);
$response = $this->controller->getInfo($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, $data['global_counter']);
$this->assertEquals(2, $data['private_counter']);
$this->assertEquals($title, $data['settings']['title']);
$this->assertEquals($headerLink, $data['settings']['header_link']);
$this->assertEquals($timezone, $data['settings']['timezone']);
$this->assertEquals($enabledPlugins, $data['settings']['enabled_plugins']);
$this->assertEquals($defaultPrivateLinks, $data['settings']['default_private_links']);
}
}

View file

@ -1,153 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceHistory;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
class DeleteLinkTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var HistoryController instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Links controller instance.
*/
protected $controller;
/** @var NoMutex */
protected $mutex;
/** @var PluginManager */
protected $pluginManager;
/**
* Before each test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$this->mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$refHistory = new ReferenceHistory();
$refHistory->write(self::$testHistory);
$this->history = new History(self::$testHistory);
$this->pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = $this->history;
$this->controller = new Links($this->container);
}
/**
* After each test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
@unlink(self::$testHistory);
}
/**
* Test DELETE link endpoint: the link should be removed.
*/
public function testDeleteLinkValid()
{
$id = '41';
$this->assertTrue($this->bookmarkService->exists($id));
$env = Environment::mock([
'REQUEST_METHOD' => 'DELETE',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->deleteLink($request, new Response(), ['id' => $id]);
$this->assertEquals(204, $response->getStatusCode());
$this->assertEmpty((string) $response->getBody());
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->assertFalse($this->bookmarkService->exists($id));
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::DELETED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
$this->assertEquals($id, $historyEntry['id']);
}
/**
* Test DELETE link endpoint: reach not existing ID.
*/
public function testDeleteLink404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
$id = -1;
$this->assertFalse($this->bookmarkService->exists($id));
$env = Environment::mock([
'REQUEST_METHOD' => 'DELETE',
]);
$request = Request::createFromEnvironment($env);
$this->controller->deleteLink($request, new Response(), ['id' => $id]);
}
}

View file

@ -1,147 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class GetLinkIdTest
*
* Test getLink by ID API service.
*
* @see http://shaarli.github.io/api-documentation/#links-link-get
*
* @package Shaarli\Api\Controllers
*/
class GetLinkIdTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Links controller instance.
*/
protected $controller;
/**
* Number of JSON fields per link.
*/
protected const NB_FIELDS_LINK = 9;
/**
* Before each test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$this->container = new Container();
$this->container['conf'] = $this->conf;
$pluginManager = new PluginManager($this->conf);
$this->container['db'] = new BookmarkFileService(
$this->conf,
$pluginManager,
$history,
$mutex,
true
);
$this->container['history'] = null;
$this->controller = new Links($this->container);
}
/**
* After each test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Test basic getLink service: return link ID=41.
*/
public function testGetLinkId()
{
// Used by index_url().
$_SERVER['SERVER_NAME'] = 'domain.tld';
$_SERVER['SERVER_PORT'] = 80;
$_SERVER['SCRIPT_NAME'] = '/';
$id = 41;
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLink($request, new Response(), ['id' => $id]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals($id, $data['id']);
// Check link elements
$this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
$this->assertEquals('WDWyig', $data['shorturl']);
$this->assertEquals('Link title: @website', $data['title']);
$this->assertEquals(
'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
$data['description']
);
$this->assertEquals('sTuff', $data['tags'][0]);
$this->assertEquals(false, $data['private']);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
$data['created']
);
$this->assertEmpty($data['updated']);
}
/**
* Test basic getLink service: get non existent link => ApiLinkNotFoundException.
*/
public function testGetLink404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
$this->expectExceptionMessage('Link not found');
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$this->controller->getLink($request, new Response(), ['id' => -1]);
}
}

View file

@ -1,492 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class GetLinksTest
*
* Test get Link list REST API service.
*
* @see http://shaarli.github.io/api-documentation/#links-links-collection-get
*
* @package Shaarli\Api\Controllers
*/
class GetLinksTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Links controller instance.
*/
protected $controller;
/**
* Number of JSON field per link.
*/
protected const NB_FIELDS_LINK = 9;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$this->container = new Container();
$this->container['conf'] = $this->conf;
$pluginManager = new PluginManager($this->conf);
$this->container['db'] = new BookmarkFileService(
$this->conf,
$pluginManager,
$history,
$mutex,
true
);
$this->container['history'] = null;
$this->controller = new Links($this->container);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Test basic getLinks service: returns all bookmarks.
*/
public function testGetLinks()
{
// Used by index_url().
$_SERVER['SERVER_NAME'] = 'domain.tld';
$_SERVER['SERVER_PORT'] = 80;
$_SERVER['SCRIPT_NAME'] = '/';
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refDB->countLinks(), count($data));
// Check order
$order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
$cpt = 0;
foreach ($data as $link) {
$this->assertEquals(self::NB_FIELDS_LINK, count($link));
$this->assertEquals($order[$cpt++], $link['id']);
}
// Check first element fields
$first = $data[2];
$this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
$this->assertEquals('WDWyig', $first['shorturl']);
$this->assertEquals('Link title: @website', $first['title']);
$this->assertEquals(
'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
$first['description']
);
$this->assertEquals('sTuff', $first['tags'][0]);
$this->assertEquals(false, $first['private']);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
$first['created']
);
$this->assertEmpty($first['updated']);
// Multi tags
$link = $data[3];
$this->assertEquals(7, count($link['tags']));
// Update date
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
$link['updated']
);
}
/**
* Test getLinks service with offset and limit parameter:
* limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
*/
public function testGetLinksOffsetLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=3&limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(8, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
}
/**
* Test getLinks with limit=all (return all link).
*/
public function testGetLinksLimitAll()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'limit=all'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refDB->countLinks(), count($data));
// Check order
$order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
$cpt = 0;
foreach ($data as $link) {
$this->assertEquals(self::NB_FIELDS_LINK, count($link));
$this->assertEquals($order[$cpt++], $link['id']);
}
}
/**
* Test getLinks service with offset and limit parameter:
* limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
*/
public function testGetLinksOffsetTooHigh()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=100'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEmpty(count($data));
}
/**
* Test getLinks with visibility parameter set to all
*/
public function testGetLinksVisibilityAll()
{
$env = Environment::mock(
[
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'visibility=all'
]
);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string)$response->getBody(), true);
$this->assertEquals($this->refDB->countLinks(), count($data));
$this->assertEquals(10, $data[0]['id']);
$this->assertEquals(41, $data[2]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
}
/**
* Test getLinks with visibility parameter set to private
*/
public function testGetLinksVisibilityPrivate()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'visibility=private'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refDB->countPrivateLinks(), count($data));
$this->assertEquals(6, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
}
/**
* Test getLinks with visibility parameter set to public
*/
public function testGetLinksVisibilityPublic()
{
$env = Environment::mock(
[
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'visibility=public'
]
);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string)$response->getBody(), true);
$this->assertEquals($this->refDB->countPublicLinks(), count($data));
$this->assertEquals(10, $data[0]['id']);
$this->assertEquals(41, $data[2]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
}
/**
* Test getLinks service with offset and limit parameter:
* limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
*/
public function testGetLinksSearchTerm()
{
// Only in description - 1 result
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=Tropical'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(1, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
// Only in tags - 1 result
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=tag3'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(0, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
// Multiple results (2)
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=stallman'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(41, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
$this->assertEquals(8, $data[1]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
// Multiword - 2 results
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=stallman+software'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(41, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
$this->assertEquals(8, $data[1]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
// URL encoding
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=' . urlencode('@web')
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(41, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
$this->assertEquals(8, $data[1]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
}
public function testGetLinksSearchTermNoResult()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=nope'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(0, count($data));
}
public function testGetLinksSearchTags()
{
// Single tag
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=dev',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(0, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
$this->assertEquals(4, $data[1]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
// Multitag + exclude
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=stuff+-gnu',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(41, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
// wildcard: placeholder at the start
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=*Tuff',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(41, $data[0]['id']);
// wildcard: placeholder at the end
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=c*',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(5, count($data));
$this->assertEquals(6, $data[0]['id']);
// wildcard: placeholder at the middle
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=w*b',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(4, count($data));
$this->assertEquals(6, $data[0]['id']);
// wildcard: match all
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=*',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data));
$this->assertEquals(10, $data[0]['id']);
$this->assertEquals(41, $data[2]['id']);
// wildcard: optional ('*' does not need to expand)
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=*stuff*',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(2, count($data));
$this->assertEquals(41, $data[0]['id']);
// wildcard: exclusions
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=*a*+-*e*',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr.
// wildcard: exclude all
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchtags=-*',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(0, count($data));
}
/**
* Test getLinks service with search tags+terms.
*/
public function testGetLinksSearchTermsAndTags()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'searchterm=poke&searchtags=dev',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getLinks($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(0, $data[0]['id']);
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
}
}

View file

@ -1,289 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceHistory;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Router;
/**
* Class PostLinkTest
*
* Test POST Link REST API service.
*
* @package Shaarli\Api\Controllers
*/
class PostLinkTest extends TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var HistoryController instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Links controller instance.
*/
protected $controller;
/**
* Number of JSON field per link.
*/
protected const NB_FIELDS_LINK = 9;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$refHistory = new ReferenceHistory();
$refHistory->write(self::$testHistory);
$this->history = new History(self::$testHistory);
$pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$pluginManager,
$this->history,
$mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = $this->history;
$this->controller = new Links($this->container);
$mock = $this->createMock(Router::class);
$mock->expects($this->any())
->method('pathFor')
->willReturn('/api/v1/bookmarks/1');
// affect @property-read... seems to work
$this->controller->getCi()->router = $mock;
// Used by index_url().
$this->controller->getCi()['environment'] = [
'SERVER_NAME' => 'domain.tld',
'SERVER_PORT' => 80,
'SCRIPT_NAME' => '/',
];
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
@unlink(self::$testHistory);
}
/**
* Test link creation without any field: creates a blank note.
*/
public function testPostLinkMinimal()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'POST',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->postLink($request, new Response());
$this->assertEquals(201, $response->getStatusCode());
$this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(43, $data['id']);
$this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
$this->assertEquals('http://domain.tld/shaare/' . $data['shorturl'], $data['url']);
$this->assertEquals('/shaare/' . $data['shorturl'], $data['title']);
$this->assertEquals('', $data['description']);
$this->assertEquals([], $data['tags']);
$this->assertEquals(true, $data['private']);
$this->assertTrue(
new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
);
$this->assertEquals('', $data['updated']);
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::CREATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
$this->assertEquals(43, $historyEntry['id']);
}
/**
* Test link creation with all available fields.
*/
public function testPostLinkFull()
{
$link = [
'url' => 'website.tld/test?foo=bar',
'title' => 'new entry',
'description' => 'shaare description',
'tags' => ['one', 'two'],
'private' => true,
'created' => '2015-05-05T12:30:00+03:00',
'updated' => '2016-06-05T14:32:10+03:00',
];
$env = Environment::mock([
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->postLink($request, new Response());
$this->assertEquals(201, $response->getStatusCode());
$this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(43, $data['id']);
$this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
$this->assertEquals('http://' . $link['url'], $data['url']);
$this->assertEquals($link['title'], $data['title']);
$this->assertEquals($link['description'], $data['description']);
$this->assertEquals($link['tags'], $data['tags']);
$this->assertEquals(true, $data['private']);
$this->assertSame($link['created'], $data['created']);
$this->assertSame($link['updated'], $data['updated']);
}
/**
* Test link creation with an existing link (duplicate URL). Should return a 409 HTTP error and the existing link.
*/
public function testPostLinkDuplicate()
{
$link = [
'url' => 'mediagoblin.org/',
'title' => 'new entry',
'description' => 'shaare description',
'tags' => ['one', 'two'],
'private' => true,
];
$env = Environment::mock([
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->postLink($request, new Response());
$this->assertEquals(409, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(7, $data['id']);
$this->assertEquals('IuWvgA', $data['shorturl']);
$this->assertEquals('http://mediagoblin.org/', $data['url']);
$this->assertEquals('MediaGoblin', $data['title']);
$this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']);
$this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
$this->assertEquals(false, $data['private']);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
);
}
/**
* Test link creation with a tag string provided
*/
public function testPostLinkWithTagString(): void
{
$link = [
'tags' => 'one two',
];
$env = Environment::mock([
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->postLink($request, new Response());
$this->assertEquals(201, $response->getStatusCode());
$this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(['one', 'two'], $data['tags']);
}
/**
* Test link creation with a tag string provided
*/
public function testPostLinkWithTagString2(): void
{
$link = [
'tags' => ['one two'],
];
$env = Environment::mock([
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->postLink($request, new Response());
$this->assertEquals(201, $response->getStatusCode());
$this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(['one', 'two'], $data['tags']);
}
}

View file

@ -1,292 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceHistory;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
class PutLinkTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var HistoryController instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Links controller instance.
*/
protected $controller;
/**
* Number of JSON field per link.
*/
protected const NB_FIELDS_LINK = 9;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$refHistory = new ReferenceHistory();
$refHistory->write(self::$testHistory);
$this->history = new History(self::$testHistory);
$pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$pluginManager,
$this->history,
$mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = $this->history;
$this->controller = new Links($this->container);
// Used by index_url().
$this->controller->getCi()['environment'] = [
'SERVER_NAME' => 'domain.tld',
'SERVER_PORT' => 80,
'SCRIPT_NAME' => '/',
];
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
@unlink(self::$testHistory);
}
/**
* Test link update without value: reset the link to default values
*/
public function testPutLinkMinimal()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$id = '41';
$request = Request::createFromEnvironment($env);
$response = $this->controller->putLink($request, new Response(), ['id' => $id]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals($id, $data['id']);
$this->assertEquals('WDWyig', $data['shorturl']);
$this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
$this->assertEquals('/shaare/WDWyig', $data['title']);
$this->assertEquals('', $data['description']);
$this->assertEquals([], $data['tags']);
$this->assertEquals(true, $data['private']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20150310_114651'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
);
$this->assertTrue(
new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
);
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
$this->assertEquals($id, $historyEntry['id']);
}
/**
* Test link update with new values
*/
public function testPutLinkWithValues()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
'CONTENT_TYPE' => 'application/json'
]);
$id = 41;
$update = [
'url' => 'http://somewhere.else',
'title' => 'Le Cid',
'description' => 'Percé jusques au fond du cœur [...]',
'tags' => ['corneille', 'rodrigue'],
'private' => true,
];
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($update);
$response = $this->controller->putLink($request, new Response(), ['id' => $id]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals($id, $data['id']);
$this->assertEquals('WDWyig', $data['shorturl']);
$this->assertEquals('http://somewhere.else', $data['url']);
$this->assertEquals('Le Cid', $data['title']);
$this->assertEquals('Percé jusques au fond du cœur [...]', $data['description']);
$this->assertEquals(['corneille', 'rodrigue'], $data['tags']);
$this->assertEquals(true, $data['private']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20150310_114651'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
);
$this->assertTrue(
new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
);
}
/**
* Test link update with an existing URL: 409 Conflict with the existing link as body
*/
public function testPutLinkDuplicate()
{
$link = [
'url' => 'mediagoblin.org/',
'title' => 'new entry',
'description' => 'shaare description',
'tags' => ['one', 'two'],
'private' => true,
];
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->putLink($request, new Response(), ['id' => 41]);
$this->assertEquals(409, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(7, $data['id']);
$this->assertEquals('IuWvgA', $data['shorturl']);
$this->assertEquals('http://mediagoblin.org/', $data['url']);
$this->assertEquals('MediaGoblin', $data['title']);
$this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']);
$this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
$this->assertEquals(false, $data['private']);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
);
$this->assertEquals(
\DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'),
\DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
);
}
/**
* Test link update on non existent link => ApiLinkNotFoundException.
*/
public function testGetLink404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
$this->expectExceptionMessage('Link not found');
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$request = Request::createFromEnvironment($env);
$this->controller->putLink($request, new Response(), ['id' => -1]);
}
/**
* Test link creation with a tag string provided
*/
public function testPutLinkWithTagString(): void
{
$link = [
'tags' => 'one two',
];
$id = '41';
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->putLink($request, new Response(), ['id' => $id]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(['one', 'two'], $data['tags']);
}
/**
* Test link creation with a tag string provided
*/
public function testPutLinkWithTagString2(): void
{
$link = [
'tags' => ['one two'],
];
$id = '41';
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
'CONTENT_TYPE' => 'application/json'
]);
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($link);
$response = $this->controller->putLink($request, new Response(), ['id' => $id]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_LINK, count($data));
$this->assertEquals(['one', 'two'], $data['tags']);
}
}

View file

@ -1,198 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceHistory;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
class DeleteTagTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var HistoryController instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Tags controller instance.
*/
protected $controller;
/** @var PluginManager */
protected $pluginManager;
/** @var NoMutex */
protected $mutex;
/**
* Before each test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$this->mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$refHistory = new ReferenceHistory();
$refHistory->write(self::$testHistory);
$this->history = new History(self::$testHistory);
$this->pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = $this->history;
$this->controller = new Tags($this->container);
}
/**
* After each test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
@unlink(self::$testHistory);
}
/**
* Test DELETE tag endpoint: the tag should be removed.
*/
public function testDeleteTagValid()
{
$tagName = 'gnu';
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertTrue($tags[$tagName] > 0);
$env = Environment::mock([
'REQUEST_METHOD' => 'DELETE',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(204, $response->getStatusCode());
$this->assertEmpty((string) $response->getBody());
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertFalse(isset($tags[$tagName]));
// 2 bookmarks affected
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
$historyEntry = $this->history->getHistory()[1];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
}
/**
* Test DELETE tag endpoint: the tag should be removed.
*/
public function testDeleteTagCaseSensitivity()
{
$tagName = 'sTuff';
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertTrue($tags[$tagName] > 0);
$env = Environment::mock([
'REQUEST_METHOD' => 'DELETE',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(204, $response->getStatusCode());
$this->assertEmpty((string) $response->getBody());
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertFalse(isset($tags[$tagName]));
$this->assertTrue($tags[strtolower($tagName)] > 0);
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
}
/**
* Test DELETE tag endpoint: reach not existing tag.
*/
public function testDeleteLink404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
$this->expectExceptionMessage('Tag not found');
$tagName = 'nopenope';
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertFalse(isset($tags[$tagName]));
$env = Environment::mock([
'REQUEST_METHOD' => 'DELETE',
]);
$request = Request::createFromEnvironment($env);
$this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]);
}
}

View file

@ -1,147 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class GetTagNameTest
*
* Test getTag by tag name API service.
*
* @package Shaarli\Api\Controllers
*/
class GetTagNameTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var Tags controller instance.
*/
protected $controller;
/** @var PluginManager */
protected $pluginManager;
/**
* Number of JSON fields per link.
*/
protected const NB_FIELDS_TAG = 2;
/**
* Before each test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->pluginManager = new PluginManager($this->conf);
$this->container['db'] = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$history,
$mutex,
true
);
$this->container['history'] = null;
$this->controller = new Tags($this->container);
}
/**
* After each test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Test basic getTag service: return gnu tag with 2 occurrences.
*/
public function testGetTag()
{
$tagName = 'gnu';
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_TAG, count($data));
$this->assertEquals($tagName, $data['name']);
$this->assertEquals(2, $data['occurrences']);
}
/**
* Test getTag service which is not case sensitive: occurrences with both sTuff and stuff
*/
public function testGetTagNotCaseSensitive()
{
$tagName = 'sTuff';
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_TAG, count($data));
$this->assertEquals($tagName, $data['name']);
$this->assertEquals(2, $data['occurrences']);
}
/**
* Test basic getTag service: get non existent tag => ApiTagNotFoundException.
*/
public function testGetTag404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
$this->expectExceptionMessage('Tag not found');
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$this->controller->getTag($request, new Response(), ['tagName' => 'nopenope']);
}
}

View file

@ -1,227 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class GetTagsTest
*
* Test get tag list REST API service.
*
* @package Shaarli\Api\Controllers
*/
class GetTagsTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var Container instance.
*/
protected $container;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var Tags controller instance.
*/
protected $controller;
/** @var PluginManager */
protected $pluginManager;
/**
* Number of JSON field per link.
*/
protected const NB_FIELDS_TAG = 2;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$this->pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$history,
$mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = null;
$this->controller = new Tags($this->container);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
}
/**
* Test basic getTags service: returns all tags.
*/
public function testGetTagsAll()
{
$tags = $this->bookmarkService->bookmarksCountPerTag();
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(count($tags), count($data));
// Check order
$this->assertEquals(self::NB_FIELDS_TAG, count($data[0]));
$this->assertEquals('web', $data[0]['name']);
$this->assertEquals(4, $data[0]['occurrences']);
$this->assertEquals(self::NB_FIELDS_TAG, count($data[1]));
$this->assertEquals('cartoon', $data[1]['name']);
$this->assertEquals(3, $data[1]['occurrences']);
// Case insensitive
$this->assertEquals(self::NB_FIELDS_TAG, count($data[5]));
$this->assertEquals('sTuff', $data[5]['name']);
$this->assertEquals(2, $data[5]['occurrences']);
// End
$this->assertEquals(self::NB_FIELDS_TAG, count($data[count($data) - 1]));
$this->assertEquals('w3c', $data[count($data) - 1]['name']);
$this->assertEquals(1, $data[count($data) - 1]['occurrences']);
}
/**
* Test getTags service with offset and limit parameter:
* limit=1 and offset=1 should return only the second tag, cartoon with 3 occurrences
*/
public function testGetTagsOffsetLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=1&limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(self::NB_FIELDS_TAG, count($data[0]));
$this->assertEquals('cartoon', $data[0]['name']);
$this->assertEquals(3, $data[0]['occurrences']);
}
/**
* Test getTags with limit=all (return all tags).
*/
public function testGetTagsLimitAll()
{
$tags = $this->bookmarkService->bookmarksCountPerTag();
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'limit=all'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(count($tags), count($data));
}
/**
* Test getTags service with offset and limit parameter:
* limit=1 and offset=1 should not return any tag
*/
public function testGetTagsOffsetTooHigh()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=100'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEmpty(count($data));
}
/**
* Test getTags with visibility parameter set to private
*/
public function testGetTagsVisibilityPrivate()
{
$tags = $this->bookmarkService->bookmarksCountPerTag([], 'private');
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'visibility=private'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(count($tags), count($data));
$this->assertEquals(self::NB_FIELDS_TAG, count($data[0]));
$this->assertEquals('Mercurial', $data[0]['name']);
$this->assertEquals(1, $data[0]['occurrences']);
}
/**
* Test getTags with visibility parameter set to public
*/
public function testGetTagsVisibilityPublic()
{
$tags = $this->bookmarkService->bookmarksCountPerTag([], 'public');
$env = Environment::mock(
[
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'visibility=public'
]
);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getTags($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string)$response->getBody(), true);
$this->assertEquals(count($tags), count($data));
$this->assertEquals(self::NB_FIELDS_TAG, count($data[0]));
$this->assertEquals('web', $data[0]['name']);
$this->assertEquals(3, $data[0]['occurrences']);
}
}

View file

@ -1,225 +0,0 @@
<?php
namespace Shaarli\Api\Controllers;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Api\Exceptions\ApiBadParametersException;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\Tests\Utils\ReferenceHistory;
use Shaarli\Tests\Utils\ReferenceLinkDB;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
class PutTagTest extends \Shaarli\TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var ReferenceLinkDB instance.
*/
protected $refDB = null;
/**
* @var HistoryController instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var BookmarkFileService instance.
*/
protected $bookmarkService;
/**
* @var Tags controller instance.
*/
protected $controller;
/** @var PluginManager */
protected $pluginManager;
/**
* Number of JSON field per link.
*/
protected const NB_FIELDS_TAG = 2;
/**
* Before every test, instantiate a new Api with its config, plugins and bookmarks.
*/
protected function setUp(): void
{
$mutex = new NoMutex();
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->conf->set('resource.datastore', self::$testDatastore);
$this->refDB = new ReferenceLinkDB();
$this->refDB->write(self::$testDatastore);
$refHistory = new ReferenceHistory();
$refHistory->write(self::$testHistory);
$this->history = new History(self::$testHistory);
$this->pluginManager = new PluginManager($this->conf);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$mutex,
true
);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = $this->bookmarkService;
$this->container['history'] = $this->history;
$this->controller = new Tags($this->container);
}
/**
* After every test, remove the test datastore.
*/
protected function tearDown(): void
{
@unlink(self::$testDatastore);
@unlink(self::$testHistory);
}
/**
* Test tags update
*/
public function testPutLinkValid()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$tagName = 'gnu';
$update = ['name' => $newName = 'newtag'];
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($update);
$response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_TAG, count($data));
$this->assertEquals($newName, $data['name']);
$this->assertEquals(2, $data['occurrences']);
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertNotTrue(isset($tags[$tagName]));
$this->assertEquals(2, $tags[$newName]);
$historyEntry = $this->history->getHistory()[0];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
$historyEntry = $this->history->getHistory()[1];
$this->assertEquals(History::UPDATED, $historyEntry['event']);
$this->assertTrue(
(new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
);
}
/**
* Test tag update with an existing tag: they should be merged
*/
public function testPutTagMerge()
{
$tagName = 'gnu';
$newName = 'w3c';
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertEquals(1, $tags[$newName]);
$this->assertEquals(2, $tags[$tagName]);
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$update = ['name' => $newName];
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($update);
$response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(self::NB_FIELDS_TAG, count($data));
$this->assertEquals($newName, $data['name']);
$this->assertEquals(3, $data['occurrences']);
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertNotTrue(isset($tags[$tagName]));
$this->assertEquals(3, $tags[$newName]);
}
/**
* Test tag update with an empty new tag name => ApiBadParametersException
*/
public function testPutTagEmpty()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiBadParametersException::class);
$this->expectExceptionMessage('New tag name is required in the request body');
$tagName = 'gnu';
$newName = '';
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertEquals(2, $tags[$tagName]);
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$request = Request::createFromEnvironment($env);
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$update = ['name' => $newName];
$request = Request::createFromEnvironment($env);
$request = $request->withParsedBody($update);
try {
$this->controller->putTag($request, new Response(), ['tagName' => $tagName]);
} catch (ApiBadParametersException $e) {
$tags = $this->bookmarkService->bookmarksCountPerTag();
$this->assertEquals(2, $tags[$tagName]);
throw $e;
}
}
/**
* Test tag update on non existent tag => ApiTagNotFoundException.
*/
public function testPutTag404()
{
$this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
$this->expectExceptionMessage('Tag not found');
$env = Environment::mock([
'REQUEST_METHOD' => 'PUT',
]);
$request = Request::createFromEnvironment($env);
$this->controller->putTag($request, new Response(), ['tagName' => 'nopenope']);
}
}

View file

@ -1,224 +0,0 @@
<?php
namespace Shaarli\Bookmark;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceLinkDB;
/**
* Class BookmarkArrayTest
*/
class BookmarkArrayTest extends TestCase
{
/**
* Test the constructor and make sure that the instance is properly initialized
*/
public function testArrayConstructorEmpty()
{
$array = new BookmarkArray();
$this->assertTrue(is_iterable($array));
$this->assertEmpty($array);
}
/**
* Test adding entries to the array, specifying the key offset or not.
*/
public function testArrayAccessAddEntries()
{
$array = new BookmarkArray();
$bookmark = new Bookmark();
$bookmark->setId(11)->validate();
$array[] = $bookmark;
$this->assertCount(1, $array);
$this->assertTrue(isset($array[11]));
$this->assertNull($array[0]);
$this->assertEquals($bookmark, $array[11]);
$bookmark = new Bookmark();
$bookmark->setId(14)->validate();
$array[14] = $bookmark;
$this->assertCount(2, $array);
$this->assertTrue(isset($array[14]));
$this->assertNull($array[0]);
$this->assertEquals($bookmark, $array[14]);
}
/**
* Test adding a bad entry: wrong type
*/
public function testArrayAccessAddBadEntryInstance()
{
$this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
$array = new BookmarkArray();
$array[] = 'nope';
}
/**
* Test adding a bad entry: no id
*/
public function testArrayAccessAddBadEntryNoId()
{
$this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
$array = new BookmarkArray();
$bookmark = new Bookmark();
$array[] = $bookmark;
}
/**
* Test adding a bad entry: no url
*/
public function testArrayAccessAddBadEntryNoUrl()
{
$this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
$array = new BookmarkArray();
$bookmark = (new Bookmark())->setId(11);
$array[] = $bookmark;
}
/**
* Test adding a bad entry: invalid offset
*/
public function testArrayAccessAddBadEntryOffset()
{
$this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
$array = new BookmarkArray();
$bookmark = (new Bookmark())->setId(11);
$bookmark->validate();
$array['nope'] = $bookmark;
}
/**
* Test adding a bad entry: ID/offset not consistent
*/
public function testArrayAccessAddBadEntryIdOffset()
{
$this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
$array = new BookmarkArray();
$bookmark = (new Bookmark())->setId(11);
$bookmark->validate();
$array[14] = $bookmark;
}
/**
* Test update entries through array access.
*/
public function testArrayAccessUpdateEntries()
{
$array = new BookmarkArray();
$bookmark = new Bookmark();
$bookmark->setId(11)->validate();
$bookmark->setTitle('old');
$array[] = $bookmark;
$bookmark = new Bookmark();
$bookmark->setId(11)->validate();
$bookmark->setTitle('test');
$array[] = $bookmark;
$this->assertCount(1, $array);
$this->assertEquals('test', $array[11]->getTitle());
$bookmark = new Bookmark();
$bookmark->setId(11)->validate();
$bookmark->setTitle('test2');
$array[11] = $bookmark;
$this->assertCount(1, $array);
$this->assertEquals('test2', $array[11]->getTitle());
}
/**
* Test delete entries through array access.
*/
public function testArrayAccessDeleteEntries()
{
$array = new BookmarkArray();
$bookmark11 = new Bookmark();
$bookmark11->setId(11)->validate();
$array[] = $bookmark11;
$bookmark14 = new Bookmark();
$bookmark14->setId(14)->validate();
$array[] = $bookmark14;
$bookmark23 = new Bookmark();
$bookmark23->setId(23)->validate();
$array[] = $bookmark23;
$bookmark0 = new Bookmark();
$bookmark0->setId(0)->validate();
$array[] = $bookmark0;
$this->assertCount(4, $array);
unset($array[14]);
$this->assertCount(3, $array);
$this->assertEquals($bookmark11, $array[11]);
$this->assertEquals($bookmark23, $array[23]);
$this->assertEquals($bookmark0, $array[0]);
unset($array[23]);
$this->assertCount(2, $array);
$this->assertEquals($bookmark11, $array[11]);
$this->assertEquals($bookmark0, $array[0]);
unset($array[11]);
$this->assertCount(1, $array);
$this->assertEquals($bookmark0, $array[0]);
unset($array[0]);
$this->assertCount(0, $array);
}
/**
* Test iterating through array access.
*/
public function testArrayAccessIterate()
{
$array = new BookmarkArray();
$bookmark11 = new Bookmark();
$bookmark11->setId(11)->validate();
$array[] = $bookmark11;
$bookmark14 = new Bookmark();
$bookmark14->setId(14)->validate();
$array[] = $bookmark14;
$bookmark23 = new Bookmark();
$bookmark23->setId(23)->validate();
$array[] = $bookmark23;
$this->assertCount(3, $array);
foreach ($array as $id => $bookmark) {
$this->assertEquals(${'bookmark' . $id}, $bookmark);
}
}
/**
* Test reordering the array.
*/
public function testReorder()
{
$refDB = new ReferenceLinkDB();
$refDB->write('sandbox/datastore.php');
$bookmarks = $refDB->getLinks();
$bookmarks->reorder('ASC');
$this->assertInstanceOf(BookmarkArray::class, $bookmarks);
$stickyIds = [11, 10];
$standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
$linkIds = array_merge($stickyIds, $standardIds);
$cpt = 0;
foreach ($bookmarks as $key => $value) {
$this->assertEquals($linkIds[$cpt++], $key);
}
$bookmarks = $refDB->getLinks();
$bookmarks->reorder('DESC');
$this->assertInstanceOf(BookmarkArray::class, $bookmarks);
$linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
$cpt = 0;
foreach ($bookmarks as $key => $value) {
$this->assertEquals($linkIds[$cpt++], $key);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,554 +0,0 @@
<?php
namespace Shaarli\Bookmark;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\FakeBookmarkService;
use Shaarli\Tests\Utils\ReferenceLinkDB;
/**
* Class BookmarkFilterTest.
*/
class BookmarkFilterTest extends TestCase
{
/**
* @var string Test datastore path.
*/
protected static $testDatastore = 'sandbox/datastore.php';
/**
* @var BookmarkFilter instance.
*/
protected static $linkFilter;
/**
* @var ReferenceLinkDB instance
*/
protected static $refDB;
/**
* @var BookmarkFileService instance
*/
protected static $bookmarkService;
/** @var PluginManager */
protected static $pluginManager;
/**
* Instantiate linkFilter with ReferenceLinkDB data.
*/
public static function setUpBeforeClass(): void
{
$mutex = new NoMutex();
$conf = new ConfigManager('tests/utils/config/configJson');
$conf->set('resource.datastore', self::$testDatastore);
static::$pluginManager = new PluginManager($conf);
self::$refDB = new ReferenceLinkDB();
self::$refDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
self::$bookmarkService = new FakeBookmarkService($conf, static::$pluginManager, $history, $mutex, true);
self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks(), $conf, static::$pluginManager);
}
/**
* Blank filter.
*/
public function testFilter()
{
$this->assertEquals(
self::$refDB->countLinks(),
count(self::$linkFilter->filter('', ''))
);
$this->assertEquals(
self::$refDB->countLinks(),
count(self::$linkFilter->filter('', '', 'all'))
);
$this->assertEquals(
self::$refDB->countLinks(),
count(self::$linkFilter->filter('', '', 'randomstr'))
);
// Private only.
$this->assertEquals(
self::$refDB->countPrivateLinks(),
count(self::$linkFilter->filter('', '', false, 'private'))
);
// Public only.
$this->assertEquals(
self::$refDB->countPublicLinks(),
count(self::$linkFilter->filter('', '', false, 'public'))
);
$this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, ''))
);
$this->assertEquals(
self::$refDB->countUntaggedLinks(),
count(
self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG,
/*$request=*/
'',
/*$casesensitive=*/
false,
/*$visibility=*/
'all',
/*$untaggedonly=*/
true
)
)
);
$this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, ''))
);
}
/**
* Filter bookmarks using a tag
*/
public function testFilterOneTag()
{
$this->assertEquals(
4,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false))
);
$this->assertEquals(
4,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '+web', false))
);
$this->assertEquals(
4,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'all'))
);
$this->assertEquals(
4,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'default-blabla'))
);
// Private only.
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'private'))
);
// Public only.
$this->assertEquals(
3,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'public'))
);
}
/**
* Filter bookmarks using a tag - case-sensitive
*/
public function testFilterCaseSensitiveTag()
{
$this->assertEquals(
0,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'mercurial', true))
);
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'Mercurial', true))
);
}
/**
* Filter bookmarks using a tag combination
*/
public function testFilterMultipleTags()
{
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'dev cartoon', false))
);
}
/**
* Filter bookmarks using a non-existent tag
*/
public function testFilterUnknownTag()
{
$this->assertEquals(
0,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'null', false))
);
}
/**
* Retrieve a link entry with its hash
*/
public function testFilterSmallHash()
{
$links = self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'IuWvgA');
$this->assertEquals(
1,
count($links)
);
$this->assertEquals(
'MediaGoblin',
$links[7]->getTitle()
);
}
/**
* No link for this hash
*/
public function testFilterUnknownSmallHash()
{
$this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah');
}
/**
* Full-text search - no result found.
*/
public function testFilterFullTextNoResult()
{
$this->assertEquals(
0,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'azertyuiop'))
);
}
/**
* Full-text search - result from a link's URL
*/
public function testFilterFullTextURL()
{
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars org'))
);
}
/**
* Full-text search - result from a link's title only
*/
public function testFilterFullTextTitle()
{
// use miscellaneous cases
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'userfriendly -'))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'UserFriendly -'))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
);
// use miscellaneous case and offset
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'RFrIendL'))
);
}
/**
* Full-text search - result from the link's description only
*/
public function testFilterFullTextDescription()
{
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'publishing media'))
);
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'mercurial w3c'))
);
$this->assertEquals(
3,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '"free software"'))
);
}
/**
* Full-text search - result from the link's tags only
*/
public function testFilterFullTextTags()
{
$this->assertEquals(
6,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web'))
);
$this->assertEquals(
6,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'all'))
);
$this->assertEquals(
6,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'bla'))
);
// Private only.
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'private'))
);
// Public only.
$this->assertEquals(
5,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'public'))
);
}
/**
* Full-text search - result set from mixed sources
*/
public function testFilterFullTextMixed()
{
$this->assertEquals(
3,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free software'))
);
}
/**
* Full-text search - test exclusion with '-'.
*/
public function testExcludeSearch()
{
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free -gnu'))
);
$this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '-revolution'))
);
}
/**
* Full-text search - test AND, exact terms and exclusion combined, across fields.
*/
public function testMultiSearch()
{
$this->assertEquals(
2,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TEXT,
'"Free Software " stallman "read this" @website stuff'
))
);
$this->assertEquals(
1,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TEXT,
'"free software " stallman "read this" -beard @website stuff'
))
);
}
/**
* Full-text search - make sure that exact search won't work across fields.
*/
public function testSearchExactTermMultiFieldsKo()
{
$this->assertEquals(
0,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TEXT,
'"designer naming"'
))
);
$this->assertEquals(
0,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TEXT,
'"designernaming"'
))
);
}
/**
* Tag search with exclusion.
*/
public function testTagFilterWithExclusion()
{
$this->assertEquals(
1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'gnu -free'))
);
$this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '-free'))
);
}
/**
* Test crossed search (terms + tags).
*/
public function testFilterCrossedSearch()
{
$terms = '"Free Software " stallman "read this" @website stuff';
$tags = 'free';
$this->assertEquals(
1,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
[$tags, $terms]
))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
['', $terms]
))
);
$this->assertEquals(
1,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
[false, 'PSR-2']
))
);
$this->assertEquals(
1,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
[$tags, '']
))
);
$this->assertEquals(
ReferenceLinkDB::$NB_LINKS_TOTAL,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
''
))
);
}
/**
* Tag search with OR optional tags.
*/
public function testTagFilterOr()
{
$this->assertEquals(
5,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web'))
);
$this->assertEquals(
6,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~c*t*n ~st*'))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web dev'))
);
$this->assertEquals(
2,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web +dev'))
);
$this->assertEquals(
4,
count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '~cartoon ~web -samba'))
);
}
/**
* Filter bookmarks by #hashtag.
*/
public function testFilterByHashtag()
{
$hashtag = 'hashtag';
$this->assertEquals(
3,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG,
$hashtag
))
);
$hashtag = 'private';
$this->assertEquals(
1,
count(self::$linkFilter->filter(
BookmarkFilter::$FILTER_TAG,
$hashtag,
false,
'private'
))
);
}
/**
* Test search result highlights in every field of bookmark reference #9.
*/
public function testFullTextSearchHighlight(): void
{
$bookmarks = self::$linkFilter->filter(
BookmarkFilter::$FILTER_TEXT,
'"psr-2" coding guide http fig "psr-2/" "This guide" basic standard. coding-style quality assurance'
);
static::assertCount(1, $bookmarks);
static::assertArrayHasKey(9, $bookmarks);
$bookmark = $bookmarks[9];
$expectedHighlights = [
'title' => [
['start' => 0, 'end' => 5], // "psr-2"
['start' => 7, 'end' => 13], // coding
['start' => 20, 'end' => 25], // guide
],
'description' => [
['start' => 0, 'end' => 10], // "This guide"
['start' => 45, 'end' => 50], // basic
['start' => 58, 'end' => 67], // standard.
],
'url' => [
['start' => 0, 'end' => 4], // http
['start' => 15, 'end' => 18], // fig
['start' => 27, 'end' => 33], // "psr-2/"
],
'tags' => [
['start' => 0, 'end' => 12], // coding-style
['start' => 23, 'end' => 30], // quality
['start' => 31, 'end' => 40], // assurance
],
];
static::assertSame($expectedHighlights, $bookmark->getAdditionalContentEntry('search_highlight'));
}
}

View file

@ -1,185 +0,0 @@
<?php
namespace Shaarli\Bookmark;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceLinkDB;
/**
* Class BookmarkInitializerTest
* @package Shaarli\Bookmark
*/
class BookmarkInitializerTest extends TestCase
{
/** @var string Path of test data store */
protected static $testDatastore = 'sandbox/datastore.php';
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/**
* @var ConfigManager instance.
*/
protected $conf;
/**
* @var History instance.
*/
protected $history;
/** @var BookmarkServiceInterface instance */
protected $bookmarkService;
/** @var BookmarkInitializer instance */
protected $initializer;
/** @var NoMutex */
protected $mutex;
/** @var PluginManager */
protected $pluginManager;
/**
* Initialize an empty BookmarkFileService
*/
public function setUp(): void
{
$this->mutex = new NoMutex();
if (file_exists(self::$testDatastore)) {
unlink(self::$testDatastore);
}
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->conf->set('resource.datastore', self::$testDatastore);
$this->pluginManager = new PluginManager($this->conf);
$this->history = new History('sandbox/history.php');
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->initializer = new BookmarkInitializer($this->bookmarkService);
}
/**
* Test initialize() with a data store containing bookmarks.
*/
public function testInitializeNotEmptyDataStore(): void
{
$refDB = new ReferenceLinkDB();
$refDB->write(self::$testDatastore);
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->initializer = new BookmarkInitializer($this->bookmarkService);
$this->initializer->initialize();
$this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
$bookmark = $this->bookmarkService->get(43);
$this->assertStringStartsWith(
'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(44);
$this->assertStringStartsWith(
'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(45);
$this->assertStringStartsWith(
'Welcome to Shaarli!',
$bookmark->getDescription()
);
$this->assertFalse($bookmark->isPrivate());
$this->bookmarkService->save();
// Reload from file
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
$bookmark = $this->bookmarkService->get(43);
$this->assertStringStartsWith(
'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(44);
$this->assertStringStartsWith(
'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(45);
$this->assertStringStartsWith(
'Welcome to Shaarli!',
$bookmark->getDescription()
);
$this->assertFalse($bookmark->isPrivate());
}
/**
* Test initialize() with an a non existent datastore file .
*/
public function testInitializeNonExistentDataStore(): void
{
$this->conf->set('resource.datastore', static::$testDatastore . '_empty');
$this->bookmarkService = new BookmarkFileService(
$this->conf,
$this->pluginManager,
$this->history,
$this->mutex,
true
);
$this->initializer->initialize();
$this->assertEquals(3, $this->bookmarkService->count());
$bookmark = $this->bookmarkService->get(0);
$this->assertStringStartsWith(
'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(1);
$this->assertStringStartsWith(
'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
$bookmark->getDescription()
);
$this->assertTrue($bookmark->isPrivate());
$bookmark = $this->bookmarkService->get(2);
$this->assertStringStartsWith(
'Welcome to Shaarli!',
$bookmark->getDescription()
);
$this->assertFalse($bookmark->isPrivate());
}
}

View file

@ -1,468 +0,0 @@
<?php
namespace Shaarli\Bookmark;
use Shaarli\Bookmark\Exception\InvalidBookmarkException;
use Shaarli\TestCase;
/**
* Class BookmarkTest
*/
class BookmarkTest extends TestCase
{
/**
* Test fromArray() with a link with full data
*/
public function testFromArrayFull()
{
$data = [
'id' => 1,
'shorturl' => 'abc',
'url' => 'https://domain.tld/oof.html?param=value#anchor',
'title' => 'This is an array link',
'description' => 'HTML desc<br><p>hi!</p>',
'thumbnail' => 'https://domain.tld/pic.png',
'sticky' => true,
'created' => new \DateTime('-1 minute'),
'tags' => ['tag1', 'tag2', 'chair'],
'updated' => new \DateTime(),
'private' => true,
];
$bookmark = (new Bookmark())->fromArray($data);
$this->assertEquals($data['id'], $bookmark->getId());
$this->assertEquals($data['shorturl'], $bookmark->getShortUrl());
$this->assertEquals($data['url'], $bookmark->getUrl());
$this->assertEquals($data['title'], $bookmark->getTitle());
$this->assertEquals($data['description'], $bookmark->getDescription());
$this->assertEquals($data['thumbnail'], $bookmark->getThumbnail());
$this->assertEquals($data['sticky'], $bookmark->isSticky());
$this->assertEquals($data['created'], $bookmark->getCreated());
$this->assertEquals($data['tags'], $bookmark->getTags());
$this->assertEquals('tag1 tag2 chair', $bookmark->getTagsString());
$this->assertEquals($data['updated'], $bookmark->getUpdated());
$this->assertEquals($data['private'], $bookmark->isPrivate());
$this->assertFalse($bookmark->isNote());
}
/**
* Test fromArray() with a link with minimal data.
* Note that I use null values everywhere but this should not happen in the real world.
*/
public function testFromArrayMinimal()
{
$data = [
'id' => null,
'shorturl' => null,
'url' => null,
'title' => null,
'description' => null,
'created' => null,
'tags' => null,
'private' => null,
];
$bookmark = (new Bookmark())->fromArray($data);
$this->assertNull($bookmark->getId());
$this->assertNull($bookmark->getShortUrl());
$this->assertNull($bookmark->getUrl());
$this->assertNull($bookmark->getTitle());
$this->assertEquals('', $bookmark->getDescription());
$this->assertNull($bookmark->getCreated());
$this->assertEquals([], $bookmark->getTags());
$this->assertEquals('', $bookmark->getTagsString());
$this->assertNull($bookmark->getUpdated());
$this->assertFalse($bookmark->getThumbnail());
$this->assertFalse($bookmark->isSticky());
$this->assertFalse($bookmark->isPrivate());
$this->assertTrue($bookmark->isNote());
}
/**
* Test fromArray() with a link with a custom tags separator
*/
public function testFromArrayCustomTagsSeparator()
{
$data = [
'id' => 1,
'tags' => ['tag1', 'tag2', 'chair'],
];
$bookmark = (new Bookmark())->fromArray($data, '@');
$this->assertEquals($data['id'], $bookmark->getId());
$this->assertEquals($data['tags'], $bookmark->getTags());
$this->assertEquals('tag1@tag2@chair', $bookmark->getTagsString('@'));
}
/**
* Test validate() with a valid minimal bookmark
*/
public function testValidateValidFullBookmark()
{
$bookmark = new Bookmark();
$bookmark->setId(2);
$bookmark->setShortUrl('abc');
$bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
$bookmark->setUpdated($dateUp = \DateTime::createFromFormat('Ymd_His', '20190514_210203'));
$bookmark->setUrl($url = 'https://domain.tld/oof.html?param=value#anchor');
$bookmark->setTitle($title = 'This is an array link');
$bookmark->setDescription($desc = 'HTML desc<br><p>hi!</p>');
$bookmark->setTags($tags = ['tag1', 'tag2', 'chair']);
$bookmark->setThumbnail($thumb = 'https://domain.tld/pic.png');
$bookmark->setPrivate(true);
$bookmark->validate();
$this->assertEquals(2, $bookmark->getId());
$this->assertEquals('abc', $bookmark->getShortUrl());
$this->assertEquals($date, $bookmark->getCreated());
$this->assertEquals($dateUp, $bookmark->getUpdated());
$this->assertEquals($url, $bookmark->getUrl());
$this->assertEquals($title, $bookmark->getTitle());
$this->assertEquals($desc, $bookmark->getDescription());
$this->assertEquals($tags, $bookmark->getTags());
$this->assertEquals(implode(' ', $tags), $bookmark->getTagsString());
$this->assertEquals($thumb, $bookmark->getThumbnail());
$this->assertTrue($bookmark->isPrivate());
$this->assertFalse($bookmark->isNote());
}
/**
* Test validate() with a valid minimal bookmark
*/
public function testValidateValidMinimalBookmark()
{
$bookmark = new Bookmark();
$bookmark->setId(1);
$bookmark->setShortUrl('abc');
$bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
$bookmark->validate();
$this->assertEquals(1, $bookmark->getId());
$this->assertEquals('abc', $bookmark->getShortUrl());
$this->assertEquals($date, $bookmark->getCreated());
$this->assertEquals('/shaare/abc', $bookmark->getUrl());
$this->assertEquals('/shaare/abc', $bookmark->getTitle());
$this->assertEquals('', $bookmark->getDescription());
$this->assertEquals([], $bookmark->getTags());
$this->assertEquals('', $bookmark->getTagsString());
$this->assertFalse($bookmark->getThumbnail());
$this->assertFalse($bookmark->isPrivate());
$this->assertTrue($bookmark->isNote());
$this->assertNull($bookmark->getUpdated());
}
/**
* Test validate() with a a bookmark without ID.
*/
public function testValidateNotValidNoId()
{
$bookmark = new Bookmark();
$bookmark->setShortUrl('abc');
$bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
$exception = null;
try {
$bookmark->validate();
} catch (InvalidBookmarkException $e) {
$exception = $e;
}
$this->assertNotNull($exception);
$this->assertContainsPolyfill('- ID: ' . PHP_EOL, $exception->getMessage());
}
/**
* Test validate() with a a bookmark without short url.
*/
public function testValidateNotValidNoShortUrl()
{
$bookmark = new Bookmark();
$bookmark->setId(1);
$bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
$bookmark->setShortUrl(null);
$exception = null;
try {
$bookmark->validate();
} catch (InvalidBookmarkException $e) {
$exception = $e;
}
$this->assertNotNull($exception);
$this->assertContainsPolyfill('- ShortUrl: ' . PHP_EOL, $exception->getMessage());
}
/**
* Test validate() with a a bookmark without created datetime.
*/
public function testValidateNotValidNoCreated()
{
$bookmark = new Bookmark();
$bookmark->setId(1);
$bookmark->setShortUrl('abc');
$bookmark->setCreated(null);
$exception = null;
try {
$bookmark->validate();
} catch (InvalidBookmarkException $e) {
$exception = $e;
}
$this->assertNotNull($exception);
$this->assertContainsPolyfill('- Created: ' . PHP_EOL, $exception->getMessage());
}
/**
* Test setId() and make sure that default fields are generated.
*/
public function testSetIdEmptyGeneratedFields()
{
$bookmark = new Bookmark();
$bookmark->setId(2);
$this->assertEquals(2, $bookmark->getId());
$this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
$this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated());
}
/**
* Test setId() and with generated fields already set.
*/
public function testSetIdSetGeneratedFields()
{
$bookmark = new Bookmark();
$bookmark->setShortUrl('abc');
$bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
$bookmark->setId(2);
$this->assertEquals(2, $bookmark->getId());
$this->assertEquals('abc', $bookmark->getShortUrl());
$this->assertEquals($date, $bookmark->getCreated());
}
/**
* Test setUrl() and make sure it accepts custom protocols
*/
public function testGetUrlWithValidProtocols()
{
$bookmark = new Bookmark();
$bookmark->setUrl($url = 'myprotocol://helloworld', ['myprotocol']);
$this->assertEquals($url, $bookmark->getUrl());
$bookmark->setUrl($url = 'https://helloworld.tld', ['myprotocol']);
$this->assertEquals($url, $bookmark->getUrl());
}
/**
* Test setUrl() and make sure it accepts custom protocols
*/
public function testGetUrlWithNotValidProtocols()
{
$bookmark = new Bookmark();
$bookmark->setUrl('myprotocol://helloworld', []);
$this->assertEquals('http://helloworld', $bookmark->getUrl());
$bookmark->setUrl($url = 'https://helloworld.tld', []);
$this->assertEquals($url, $bookmark->getUrl());
}
/**
* Test addTag() and DeleteTag()
*/
public function testAddDeleteTags()
{
$bookmark = new Bookmark();
$bookmark->addTag('tag1');
$this->assertEquals(
[
'tag1',
],
$bookmark->getTags()
);
// Ignore if tag is already present
$bookmark->addTag('tag2');
$bookmark->addTag('tag1');
$this->assertEquals(
[
'tag1',
'tag2',
],
$bookmark->getTags()
);
// Ignore deleting tags not present
$bookmark->deleteTag('tag5');
$this->assertEquals(
[
'tag1',
'tag2',
],
$bookmark->getTags()
);
// Delete multiples
$bookmark->setTags(['tag3', 'tag1', 'tag4', 'tag3', 'tag3', 'tag4']);
$bookmark->deleteTag('tag3');
$this->assertEquals(
[
'tag1',
'tag4',
'tag4',
],
$bookmark->getTags()
);
$bookmark->deleteTag('tag4');
$this->assertEquals(
[
'tag1',
],
$bookmark->getTags()
);
}
/**
* Test setTagsString() with exotic data
*/
public function testSetTagsString()
{
$bookmark = new Bookmark();
$str = 'tag1 tag2 tag3.tag3-2 tag4 -tag5 ';
$bookmark->setTagsString($str);
$this->assertEquals(
[
'tag1',
'tag2',
'tag3.tag3-2',
'tag4',
'tag5',
],
$bookmark->getTags()
);
}
/**
* Test setTags() with exotic data
*/
public function testSetTags()
{
$bookmark = new Bookmark();
$array = [
'tag1 ',
' tag2',
'tag3.tag3-2',
' tag4',
' ',
'-tag5 ',
];
$bookmark->setTags($array);
$this->assertEquals(
[
'tag1',
'tag2',
'tag3.tag3-2',
'tag4',
'tag5',
],
$bookmark->getTags()
);
}
/**
* Test renameTag()
*/
public function testRenameTag()
{
$bookmark = new Bookmark();
$bookmark->setTags(['tag1', 'tag2', 'chair']);
$bookmark->renameTag('chair', 'table');
$this->assertEquals(['tag1', 'tag2', 'table'], $bookmark->getTags());
$bookmark->renameTag('tag1', 'tag42');
$this->assertEquals(['tag42', 'tag2', 'table'], $bookmark->getTags());
$bookmark->renameTag('tag42', 'tag43');
$this->assertEquals(['tag43', 'tag2', 'table'], $bookmark->getTags());
$bookmark->renameTag('table', 'desk');
$this->assertEquals(['tag43', 'tag2', 'desk'], $bookmark->getTags());
}
/**
* Test renameTag() with a tag that is not present in the bookmark
*/
public function testRenameTagNotExists()
{
$bookmark = new Bookmark();
$bookmark->setTags(['tag1', 'tag2', 'chair']);
$bookmark->renameTag('nope', 'table');
$this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
}
/**
* Test deleteTag()
*/
public function testDeleteTag()
{
$bookmark = new Bookmark();
$bookmark->setTags(['tag1', 'tag2', 'chair']);
$bookmark->deleteTag('chair');
$this->assertEquals(['tag1', 'tag2'], $bookmark->getTags());
$bookmark->deleteTag('tag1');
$this->assertEquals(['tag2'], $bookmark->getTags());
$bookmark->deleteTag('tag2');
$this->assertEquals([], $bookmark->getTags());
}
/**
* Test deleteTag() with a tag that is not present in the bookmark
*/
public function testDeleteTagNotExists()
{
$bookmark = new Bookmark();
$bookmark->setTags(['tag1', 'tag2', 'chair']);
$bookmark->deleteTag('nope');
$this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
}
/**
* Test shouldUpdateThumbnail() with bookmarks needing an update.
*/
public function testShouldUpdateThumbnail(): void
{
$bookmark = (new Bookmark())->setUrl('http://domain.tld/with-image');
static::assertTrue($bookmark->shouldUpdateThumbnail());
$bookmark = (new Bookmark())
->setUrl('http://domain.tld/with-image')
->setThumbnail('unknown file')
;
static::assertTrue($bookmark->shouldUpdateThumbnail());
}
/**
* Test shouldUpdateThumbnail() with bookmarks that should not update.
*/
public function testShouldNotUpdateThumbnail(): void
{
$bookmark = (new Bookmark());
static::assertFalse($bookmark->shouldUpdateThumbnail());
$bookmark = (new Bookmark())
->setUrl('ftp://domain.tld/other-protocol', ['ftp'])
;
static::assertFalse($bookmark->shouldUpdateThumbnail());
$bookmark = (new Bookmark())
->setUrl('http://domain.tld/with-image')
->setThumbnail(__FILE__)
;
static::assertFalse($bookmark->shouldUpdateThumbnail());
$bookmark = (new Bookmark())->setUrl('/shaare/abcdef');
static::assertFalse($bookmark->shouldUpdateThumbnail());
}
}

View file

@ -1,767 +0,0 @@
<?php
namespace Shaarli\Bookmark;
use Shaarli\TestCase;
/**
* Class LinkUtilsTest.
*/
class LinkUtilsTest extends TestCase
{
/**
* Test html_extract_title() when the title is found.
*/
public function testHtmlExtractExistentTitle()
{
$title = 'Read me please.';
$html = '<html><meta>stuff</meta><title>' . $title . '</title></html>';
$this->assertEquals($title, html_extract_title($html));
$html = '<html><title>' . $title . '</title>blabla<title>another</title></html>';
$this->assertEquals($title, html_extract_title($html));
}
/**
* Test html_extract_title() when the title is not found.
*/
public function testHtmlExtractNonExistentTitle()
{
$html = '<html><meta>stuff</meta></html>';
$this->assertFalse(html_extract_title($html));
}
/**
* Test headers_extract_charset() when the charset is found.
*/
public function testHeadersExtractExistentCharset()
{
$charset = 'x-MacCroatian';
$headers = 'text/html; charset=' . $charset;
$this->assertEquals(strtolower($charset), header_extract_charset($headers));
}
/**
* Test headers_extract_charset() when the charset is found with odd quotes.
*/
public function testHeadersExtractExistentCharsetWithQuotes()
{
$charset = 'x-MacCroatian';
$headers = 'text/html; charset="' . $charset . '"otherstuff="test"';
$this->assertEquals(strtolower($charset), header_extract_charset($headers));
$headers = 'text/html; charset=\'' . $charset . '\'otherstuff="test"';
$this->assertEquals(strtolower($charset), header_extract_charset($headers));
}
/**
* Test headers_extract_charset() when the charset is not found.
*/
public function testHeadersExtractNonExistentCharset()
{
$headers = '';
$this->assertFalse(header_extract_charset($headers));
$headers = 'text/html';
$this->assertFalse(header_extract_charset($headers));
}
/**
* Test html_extract_charset() when the charset is found.
*/
public function testHtmlExtractExistentCharset()
{
$charset = 'x-MacCroatian';
$html = '<html><meta>stuff2</meta><meta charset="' . $charset . '"/></html>';
$this->assertEquals(strtolower($charset), html_extract_charset($html));
}
/**
* Test html_extract_charset() when the charset is not found.
*/
public function testHtmlExtractNonExistentCharset()
{
$html = '<html><meta>stuff</meta></html>';
$this->assertFalse(html_extract_charset($html));
$html = '<html><meta>stuff</meta><meta charset=""/></html>';
$this->assertFalse(html_extract_charset($html));
}
/**
* Test html_extract_tag() when the tag <meta name= is found.
*/
public function testHtmlExtractExistentNameTag()
{
$description = 'Bob and Alice share cookies.';
// Simple one line
$html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>';
$this->assertEquals($description, html_extract_tag('description', $html));
// Simple OpenGraph
$html = '<meta property="og:description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// Simple reversed OpenGraph
$html = '<meta content="' . $description . '" property="og:description">';
$this->assertEquals($description, html_extract_tag('description', $html));
// ItemProp OpenGraph
$html = '<meta itemprop="og:description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph without quotes
$html = '<meta property=og:description content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed without quotes
$html = '<meta content="' . $description . '" property=og:description>';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph with noise
$html = '<meta tag1="content1" property="og:description" tag2="content2" content="' .
$description . '" tag3="content3">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed with noise
$html = '<meta tag1="content1" content="' . $description . '" ' .
'tag3="content3" tag2="content2" property="og:description">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph multiple properties start
$html = '<meta property="unrelated og:description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph multiple properties end
$html = '<meta property="og:description unrelated" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph multiple properties both end
$html = '<meta property="og:unrelated1 og:description og:unrelated2" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph multiple properties both end with noise
$html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" ' .
'tag2="content2" content="' . $description . '" tag3="content3">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed multiple properties start
$html = '<meta content="' . $description . '" property="unrelated og:description">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed multiple properties end
$html = '<meta content="' . $description . '" property="og:description unrelated">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed multiple properties both end
$html = '<meta content="' . $description . '" property="og:unrelated1 og:description og:unrelated2">';
$this->assertEquals($description, html_extract_tag('description', $html));
// OpenGraph reversed multiple properties both end with noise
$html = '<meta tag1="content1" content="' . $description . '" tag2="content2" ' .
'property="og:unrelated1 og:description og:unrelated2" tag3="content3">';
$this->assertEquals($description, html_extract_tag('description', $html));
// Suggestion from #1375
$html = '<meta property="og:description" name="description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
}
/**
* Test html_extract_tag() with double quoted content containing single quote, and the opposite.
*/
public function testHtmlExtractExistentNameTagWithMixedQuotes(): void
{
$description = 'Bob and Alice share M&M\'s.';
$html = '<meta property="og:description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
$html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" ' .
'tag2="content2" content="' . $description . '" tag3="content3">';
$this->assertEquals($description, html_extract_tag('description', $html));
$html = '<meta property="og:description" name="description" content="' . $description . '">';
$this->assertEquals($description, html_extract_tag('description', $html));
$description = 'Bob and Alice share "cookies".';
$html = '<meta property="og:description" content=\'' . $description . '\'>';
$this->assertEquals($description, html_extract_tag('description', $html));
$html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" ' .
'tag2="content2" content=\'' . $description . '\' tag3="content3">';
$this->assertEquals($description, html_extract_tag('description', $html));
$html = '<meta property="og:description" name="description" content=\'' . $description . '\'>';
$this->assertEquals($description, html_extract_tag('description', $html));
}
/**
* Test html_extract_tag() when the tag <meta name= is not found.
*/
public function testHtmlExtractNonExistentNameTag()
{
$html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
$this->assertFalse(html_extract_tag('description', $html));
// Partial meta tag
$html = '<meta content="Brief description">';
$this->assertFalse(html_extract_tag('description', $html));
$html = '<meta property="og:description">';
$this->assertFalse(html_extract_tag('description', $html));
$html = '<meta tag1="content1" property="og:description">';
$this->assertFalse(html_extract_tag('description', $html));
$html = '<meta property="og:description" tag1="content1">';
$this->assertFalse(html_extract_tag('description', $html));
$html = '<meta tag1="content1" content="Brief description">';
$this->assertFalse(html_extract_tag('description', $html));
$html = '<meta content="Brief description" tag1="content1">';
$this->assertFalse(html_extract_tag('description', $html));
}
/**
* Test html_extract_tag() when the tag <meta property="og: is found.
*/
public function testHtmlExtractExistentOgTag()
{
$description = 'Bob and Alice share cookies.';
$html = '<html><meta>stuff2</meta><meta property="og:description" content="' . $description . '"/></html>';
$this->assertEquals($description, html_extract_tag('description', $html));
}
/**
* Test html_extract_tag() when the tag <meta property="og: is not found.
*/
public function testHtmlExtractNonExistentOgTag()
{
$html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
$this->assertFalse(html_extract_tag('description', $html));
}
public function testHtmlExtractDescriptionFromGoogleRealCase(): void
{
$html = 'id="gsr"><meta content="Fêtes de fin d\'année" property="twitter:title"><meta ' .
'content="Bonnes fêtes de fin d\'année ! #GoogleDoodle" property="twitter:description">' .
'<meta content="Bonnes fêtes de fin d\'année ! #GoogleDoodle" property="og:description">' .
'<meta content="summary_large_image" property="twitter:card"><meta co'
;
$this->assertSame('Bonnes fêtes de fin d\'année ! #GoogleDoodle', html_extract_tag('description', $html));
}
/**
* Test the header callback with valid value
*/
public function testCurlHeaderCallbackOk(): void
{
$callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ok');
$data = [
'HTTP/1.1 200 OK',
'Server: GitHub.com',
'Date: Sat, 28 Oct 2017 12:01:33 GMT',
'Content-Type: text/html; charset=utf-8',
'Status: 200 OK',
];
foreach ($data as $chunk) {
static::assertIsInt($callback(null, $chunk));
}
static::assertSame('utf-8', $charset);
}
/**
* Test the download callback with valid value
*/
public function testCurlDownloadCallbackOk(): void
{
$charset = 'utf-8';
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
false,
' '
);
$data = [
'th=device-width">'
. '<title>Refactoring · GitHub</title>'
. '<link rel="search" type="application/opensea',
'<title>ignored</title>'
. '<meta name="description" content="desc" />'
. '<meta name="keywords" content="key1,key2" />',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
static::assertSame('utf-8', $charset);
static::assertSame('Refactoring · GitHub', $title);
static::assertEmpty($desc);
static::assertEmpty($keywords);
}
/**
* Test the header callback with valid value
*/
public function testCurlHeaderCallbackNoCharset(): void
{
$callback = get_curl_header_callback($charset, 'ut_curl_getinfo_no_charset');
$data = [
'HTTP/1.1 200 OK',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
static::assertFalse($charset);
}
/**
* Test the download callback with valid values and no charset
*/
public function testCurlDownloadCallbackOkNoCharset(): void
{
$charset = null;
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
false,
' '
);
$data = [
'end' => 'th=device-width">'
. '<title>Refactoring · GitHub</title>'
. '<link rel="search" type="application/opensea',
'<title>ignored</title>'
. '<meta name="description" content="desc" />'
. '<meta name="keywords" content="key1,key2" />',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
$this->assertEmpty($charset);
$this->assertEquals('Refactoring · GitHub', $title);
$this->assertEmpty($desc);
$this->assertEmpty($keywords);
}
/**
* Test the download callback with valid values and no charset
*/
public function testCurlDownloadCallbackOkHtmlCharset(): void
{
$charset = null;
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
false,
' '
);
$data = [
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
'end' => 'th=device-width">'
. '<title>Refactoring · GitHub</title>'
. '<link rel="search" type="application/opensea',
'<title>ignored</title>'
. '<meta name="description" content="desc" />'
. '<meta name="keywords" content="key1,key2" />',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
$this->assertEquals('utf-8', $charset);
$this->assertEquals('Refactoring · GitHub', $title);
$this->assertEmpty($desc);
$this->assertEmpty($keywords);
}
/**
* Test the download callback with valid values and no title
*/
public function testCurlDownloadCallbackOkNoTitle(): void
{
$charset = 'utf-8';
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
false,
' '
);
$data = [
'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea',
'ignored',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
$this->assertEquals('utf-8', $charset);
$this->assertEmpty($title);
$this->assertEmpty($desc);
$this->assertEmpty($keywords);
}
/**
* Test the header callback with an invalid content type.
*/
public function testCurlHeaderCallbackInvalidContentType(): void
{
$callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ct_ko');
$data = [
'HTTP/1.1 200 OK',
];
static::assertFalse($callback(null, $data[0]));
static::assertNull($charset);
}
/**
* Test the header callback with an invalid response code.
*/
public function testCurlHeaderCallbackInvalidResponseCode(): void
{
$callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rc_ko');
static::assertFalse($callback(null, ''));
static::assertNull($charset);
}
/**
* Test the header callback with an invalid content type and response code.
*/
public function testCurlHeaderCallbackInvalidContentTypeAndResponseCode(): void
{
$callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rs_ct_ko');
static::assertFalse($callback(null, ''));
static::assertNull($charset);
}
/**
* Test the download callback with valid value, and retrieve_description option enabled.
*/
public function testCurlDownloadCallbackOkWithDesc(): void
{
$charset = 'utf-8';
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
true,
' '
);
$data = [
'th=device-width">'
. '<title>Refactoring · GitHub</title>'
. '<link rel="search" type="application/opensea',
'end' => '<title>ignored</title>'
. '<meta name="description" content="link desc" />'
. '<meta name="keywords" content="key1,key2" />',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
$this->assertEquals('utf-8', $charset);
$this->assertEquals('Refactoring · GitHub', $title);
$this->assertEquals('link desc', $desc);
$this->assertEquals('key1 key2', $keywords);
}
/**
* Test the download callback with valid value, and retrieve_description option enabled,
* but no desc or keyword defined in the page.
*/
public function testCurlDownloadCallbackOkWithDescNotFound(): void
{
$charset = 'utf-8';
$callback = get_curl_download_callback(
$charset,
$title,
$desc,
$keywords,
true,
'ut_curl_getinfo_ok'
);
$data = [
'th=device-width">'
. '<title>Refactoring · GitHub</title>'
. '<link rel="search" type="application/opensea',
'end' => '<title>ignored</title>',
];
foreach ($data as $chunk) {
static::assertSame(strlen($chunk), $callback(null, $chunk));
}
$this->assertEquals('utf-8', $charset);
$this->assertEquals('Refactoring · GitHub', $title);
$this->assertEmpty($desc);
$this->assertEmpty($keywords);
}
/**
* Test text2clickable.
*/
public function testText2clickable()
{
$text = 'stuff http://hello.there/is=someone#here otherstuff';
$expectedText = 'stuff <a href="http://hello.there/is=someone#here">'
. 'http://hello.there/is=someone#here</a> otherstuff';
$processedText = text2clickable($text);
$this->assertEquals($expectedText, $processedText);
$text = 'stuff http://hello.there/is=someone#here(please) otherstuff';
$expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">'
. 'http://hello.there/is=someone#here(please)</a> otherstuff';
$processedText = text2clickable($text);
$this->assertEquals($expectedText, $processedText);
$text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
$text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
$expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">'
. 'http://hello.there/is=someone#here(please)&no</a> otherstuff';
$processedText = text2clickable($text);
$this->assertEquals($expectedText, $processedText);
}
/**
* Test testSpace2nbsp.
*/
public function testSpace2nbsp()
{
$text = ' Are you thrilled by flags ?' . PHP_EOL . ' Really?';
$expectedText = '&nbsp; Are you &nbsp; thrilled &nbsp;by flags &nbsp; ?' . PHP_EOL . '&nbsp;Really?';
$processedText = space2nbsp($text);
$this->assertEquals($expectedText, $processedText);
}
/**
* Test hashtags auto-link.
*/
public function testHashtagAutolink()
{
$index = 'http://domain.tld/';
$rawDescription = '#hashtag\n
# nothashtag\n
test#nothashtag #hashtag \#nothashtag\n
test #hashtag #hashtag test #hashtag.test\n
#hashtag #hashtag-nothashtag #hashtag_hashtag\n
What is #ашок anyway?\n
カタカナ #カタカナ」カタカナ\n';
$autolinkedDescription = hashtag_autolink($rawDescription, $index);
$this->assertContainsPolyfill($this->getHashtagLink('hashtag', $index), $autolinkedDescription);
$this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
$this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
$this->assertContainsPolyfill($this->getHashtagLink('ашок', $index), $autolinkedDescription);
$this->assertContainsPolyfill($this->getHashtagLink('カタカナ', $index), $autolinkedDescription);
$this->assertContainsPolyfill($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription);
$this->assertNotContainsPolyfill($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription);
}
/**
* Test hashtags auto-link without index URL.
*/
public function testHashtagAutolinkNoIndex()
{
$rawDescription = 'blabla #hashtag x#nothashtag';
$autolinkedDescription = hashtag_autolink($rawDescription);
$this->assertContainsPolyfill($this->getHashtagLink('hashtag'), $autolinkedDescription);
$this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
$this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
}
/**
* Test is_note with note URLs.
*/
public function testIsNote()
{
$this->assertTrue(is_note('?'));
$this->assertTrue(is_note('?abcDEf'));
$this->assertTrue(is_note('?_abcDEf#123'));
}
/**
* Test is_note with non note URLs.
*/
public function testIsNotNote()
{
$this->assertFalse(is_note(''));
$this->assertFalse(is_note('nope'));
$this->assertFalse(is_note('https://github.com/shaarli/Shaarli/?hi'));
}
/**
* Test tags_str2array with whitespace separator.
*/
public function testTagsStr2ArrayWithSpaceSeparator(): void
{
$separator = ' ';
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array(' tag1 tag2 tag3 ', $separator));
static::assertSame(['tag1@', 'tag2,', '.tag3'], tags_str2array(' tag1@ tag2, .tag3 ', $separator));
static::assertSame([], tags_str2array('', $separator));
static::assertSame([], tags_str2array(' ', $separator));
static::assertSame([], tags_str2array(null, $separator));
}
/**
* Test tags_str2array with @ separator.
*/
public function testTagsStr2ArrayWithCharSeparator(): void
{
$separator = '@';
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@tag2@tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@@@@tag2@@@@tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('@@@tag1@@@tag2@@@@tag3@@', $separator));
static::assertSame(
['tag1#', 'tag2, and other', '.tag3'],
tags_str2array('@@@ tag1# @@@ tag2, and other @@@@.tag3@@', $separator)
);
static::assertSame([], tags_str2array('', $separator));
static::assertSame([], tags_str2array(' ', $separator));
static::assertSame([], tags_str2array(null, $separator));
}
/**
* Test tags_str2array with / separator.
*/
public function testTagsStr2ArrayWithRegexDelimiterSeparator(): void
{
$separator = '/';
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1/tag2/tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1////tag2////tag3', $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('///tag1///tag2////tag3//', $separator));
static::assertSame(
['tag1#', 'tag2, and other', '.tag3'],
tags_str2array('/// tag1# /// tag2, and other ////.tag3//', $separator)
);
static::assertSame([], tags_str2array('', $separator));
static::assertSame([], tags_str2array(' ', $separator));
static::assertSame([], tags_str2array(null, $separator));
}
/**
* Test tags_array2str with ' ' separator.
*/
public function testTagsArray2StrWithSpaceSeparator(): void
{
$separator = ' ';
static::assertSame('tag1 tag2 tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator));
static::assertSame('tag1, tag2@ tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator));
static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', 'tag2', 'tag3 '], $separator));
static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator));
static::assertSame('tag1', tags_array2str([' tag1 '], $separator));
static::assertSame('', tags_array2str([' '], $separator));
static::assertSame('', tags_array2str([], $separator));
static::assertSame('', tags_array2str(null, $separator));
}
/**
* Test tags_array2str with @ separator.
*/
public function testTagsArray2StrWithCharSeparator(): void
{
$separator = '@';
static::assertSame('tag1@tag2@tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator));
static::assertSame('tag1,@tag2@tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator));
static::assertSame(
'tag1@tag2, and other@tag3',
tags_array2str(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator)
);
static::assertSame('tag1@tag2@tag3', tags_array2str(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator));
static::assertSame('tag1', tags_array2str(['@@@@tag1@@@@'], $separator));
static::assertSame('', tags_array2str(['@@@'], $separator));
static::assertSame('', tags_array2str([], $separator));
static::assertSame('', tags_array2str(null, $separator));
}
/**
* Test tags_array2str with @ separator.
*/
public function testTagsFilterWithSpaceSeparator(): void
{
$separator = ' ';
static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator));
static::assertSame(['tag1,', 'tag2@', 'tag3'], tags_filter(['tag1,', 'tag2@', 'tag3'], $separator));
static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter([' tag1 ', 'tag2', 'tag3 '], $separator));
static::assertSame(
['tag1', 'tag2', 'tag3'],
tags_filter([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator)
);
static::assertSame(['tag1'], tags_filter([' tag1 '], $separator));
static::assertSame([], tags_filter([' '], $separator));
static::assertSame([], tags_filter([], $separator));
static::assertSame([], tags_filter(null, $separator));
}
/**
* Test tags_array2str with @ separator.
*/
public function testTagsArrayFilterWithSpaceSeparator(): void
{
$separator = '@';
static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator));
static::assertSame(['tag1,', 'tag2#', 'tag3'], tags_filter(['tag1,', 'tag2#', 'tag3'], $separator));
static::assertSame(
['tag1', 'tag2, and other', 'tag3'],
tags_filter(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator)
);
static::assertSame(
['tag1', 'tag2', 'tag3'],
tags_filter(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator)
);
static::assertSame(['tag1'], tags_filter(['@@@@tag1@@@@'], $separator));
static::assertSame([], tags_filter(['@@@'], $separator));
static::assertSame([], tags_filter([], $separator));
static::assertSame([], tags_filter(null, $separator));
}
/**
* Util function to build an hashtag link.
*
* @param string $hashtag Hashtag name.
* @param string $index Index URL.
*
* @return string HTML hashtag link.
*/
private function getHashtagLink($hashtag, $index = '')
{
$hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
return str_replace('$1', $hashtag, $hashtagLink);
}
}

View file

@ -1,125 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Bookmark;
use Shaarli\TestCase;
/**
* Test SearchResult class.
*/
class SearchResultTest extends TestCase
{
/** Create a SearchResult without any pagination parameter. */
public function testResultNoParameters(): void
{
$searchResult = SearchResult::getSearchResult($data = ['a', 'b', 'c', 'd', 'e', 'f']);
static::assertSame($data, $searchResult->getBookmarks());
static::assertSame(6, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(null, $searchResult->getLimit());
static::assertSame(0, $searchResult->getOffset());
static::assertSame(1, $searchResult->getPage());
static::assertSame(1, $searchResult->getLastPage());
static::assertTrue($searchResult->isFirstPage());
static::assertTrue($searchResult->isLastPage());
}
/** Create a SearchResult with only an offset parameter */
public function testResultWithOffset(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2);
static::assertSame([2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f'], $searchResult->getBookmarks());
static::assertSame(4, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(null, $searchResult->getLimit());
static::assertSame(2, $searchResult->getOffset());
static::assertSame(2, $searchResult->getPage());
static::assertSame(2, $searchResult->getLastPage());
static::assertFalse($searchResult->isFirstPage());
static::assertTrue($searchResult->isLastPage());
}
/** Create a SearchResult with only a limit parameter */
public function testResultWithLimit(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 0, 2);
static::assertSame([0 => 'a', 1 => 'b'], $searchResult->getBookmarks());
static::assertSame(2, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(2, $searchResult->getLimit());
static::assertSame(0, $searchResult->getOffset());
static::assertSame(1, $searchResult->getPage());
static::assertSame(3, $searchResult->getLastPage());
static::assertTrue($searchResult->isFirstPage());
static::assertFalse($searchResult->isLastPage());
}
/** Create a SearchResult with offset and limit parameters */
public function testResultWithLimitAndOffset(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2, 2);
static::assertSame([2 => 'c', 3 => 'd'], $searchResult->getBookmarks());
static::assertSame(2, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(2, $searchResult->getLimit());
static::assertSame(2, $searchResult->getOffset());
static::assertSame(2, $searchResult->getPage());
static::assertSame(3, $searchResult->getLastPage());
static::assertFalse($searchResult->isFirstPage());
static::assertFalse($searchResult->isLastPage());
}
/** Create a SearchResult with offset and limit parameters displaying the last page */
public function testResultWithLimitAndOffsetLastPage(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 4, 2);
static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks());
static::assertSame(2, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(2, $searchResult->getLimit());
static::assertSame(4, $searchResult->getOffset());
static::assertSame(3, $searchResult->getPage());
static::assertSame(3, $searchResult->getLastPage());
static::assertFalse($searchResult->isFirstPage());
static::assertTrue($searchResult->isLastPage());
}
/** Create a SearchResult with offset and limit parameters out of bound (display the last page) */
public function testResultWithLimitAndOffsetOutOfBounds(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2);
static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks());
static::assertSame(2, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(2, $searchResult->getLimit());
static::assertSame(-2, $searchResult->getOffset());
static::assertSame(3, $searchResult->getPage());
static::assertSame(3, $searchResult->getLastPage());
static::assertFalse($searchResult->isFirstPage());
static::assertTrue($searchResult->isLastPage());
}
/** Create a SearchResult with offset and limit parameters out of bound (no result) */
public function testResultWithLimitAndOffsetOutOfBoundsNoResult(): void
{
$searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2, true);
static::assertSame([], $searchResult->getBookmarks());
static::assertSame(0, $searchResult->getResultCount());
static::assertSame(6, $searchResult->getTotalCount());
static::assertSame(2, $searchResult->getLimit());
static::assertSame(12, $searchResult->getOffset());
static::assertSame(7, $searchResult->getPage());
static::assertSame(3, $searchResult->getLastPage());
static::assertFalse($searchResult->isFirstPage());
static::assertFalse($searchResult->isLastPage());
}
}

View file

@ -1,45 +0,0 @@
<?php
require_once 'vendor/autoload.php';
use Shaarli\Tests\Utils\ReferenceSessionIdHashes;
$conf = new \Shaarli\Config\ConfigManager('tests/utils/config/configJson');
new \Shaarli\Languages('en', $conf);
// is_iterable is only compatible with PHP 7.1+
if (!function_exists('is_iterable')) {
function is_iterable($var)
{
return is_array($var) || $var instanceof \Traversable;
}
}
// raw functions
require_once 'application/config/ConfigPlugin.php';
require_once 'application/bookmark/LinkUtils.php';
require_once 'application/http/UrlUtils.php';
require_once 'application/http/HttpUtils.php';
require_once 'application/Utils.php';
require_once 'application/TimeZone.php';
require_once 'tests/utils/CurlUtils.php';
require_once 'tests/utils/RainTPL.php';
// TODO: remove this after fixing UT
require_once 'tests/TestCase.php';
require_once 'tests/container/ShaarliTestContainer.php';
require_once 'tests/front/controller/visitor/FrontControllerMockHelper.php';
require_once 'tests/front/controller/admin/FrontAdminControllerMockHelper.php';
require_once 'tests/updater/DummyUpdater.php';
require_once 'tests/utils/FakeApplicationUtils.php';
require_once 'tests/utils/FakeBookmarkService.php';
require_once 'tests/utils/FakeConfigManager.php';
require_once 'tests/utils/ReferenceHistory.php';
require_once 'tests/utils/ReferenceLinkDB.php';
require_once 'tests/utils/ReferenceSessionIdHashes.php';
ReferenceSessionIdHashes::genAllHashes();
if (!defined('SHAARLI_MUTEX_FILE')) {
define('SHAARLI_MUTEX_FILE', __FILE__);
}

View file

@ -1,121 +0,0 @@
<?php
namespace Shaarli\Config;
/**
* Class ConfigJsonTest
*/
class ConfigJsonTest extends \Shaarli\TestCase
{
/**
* @var ConfigJson
*/
protected $configIO;
protected function setUp(): void
{
$this->configIO = new ConfigJson();
}
/**
* Read a simple existing config file.
*/
public function testRead()
{
$conf = $this->configIO->read('tests/utils/config/configJson.json.php');
$this->assertEquals('root', $conf['credentials']['login']);
$this->assertEquals('lala', $conf['redirector']['url']);
$this->assertEquals('sandbox/datastore.php', $conf['resource']['datastore']);
$this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']);
}
/**
* Read a non existent config file -> empty array.
*/
public function testReadNonExistent()
{
$this->assertEquals(array(), $this->configIO->read('nope'));
}
/**
* Read a non existent config file -> empty array.
*/
public function testReadInvalidJson()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessageRegExp('/An error occurred while parsing JSON configuration file \\([\\w\\/\\.]+\\): error code #4/');
$this->configIO->read('tests/utils/config/configInvalid.json.php');
}
/**
* Write a new config file.
*/
public function testWriteNew()
{
$dataFile = 'tests/utils/config/configWrite.json.php';
$data = array(
'credentials' => array(
'login' => 'root',
),
'resource' => array(
'datastore' => 'data/datastore.php',
),
'redirector' => array(
'url' => 'lala',
),
'plugins' => array(
'WALLABAG_VERSION' => '1',
)
);
$this->configIO->write($dataFile, $data);
// PHP 5.3 doesn't support json pretty print.
if (defined('JSON_PRETTY_PRINT')) {
$expected = '{
"credentials": {
"login": "root"
},
"resource": {
"datastore": "data\/datastore.php"
},
"redirector": {
"url": "lala"
},
"plugins": {
"WALLABAG_VERSION": "1"
}
}';
} else {
$expected = '{"credentials":{"login":"root"},"resource":{"datastore":"data\/datastore.php"},"redirector":{"url":"lala"},"plugins":{"WALLABAG_VERSION":"1"}}';
}
$expected = ConfigJson::getPhpHeaders() . $expected . ConfigJson::getPhpSuffix();
$this->assertEquals($expected, file_get_contents($dataFile));
unlink($dataFile);
}
/**
* Overwrite an existing setting.
*/
public function testOverwrite()
{
$source = 'tests/utils/config/configJson.json.php';
$dest = 'tests/utils/config/configOverwrite.json.php';
copy($source, $dest);
$conf = $this->configIO->read($dest);
$conf['redirector']['url'] = 'blabla';
$this->configIO->write($dest, $conf);
$conf = $this->configIO->read($dest);
$this->assertEquals('blabla', $conf['redirector']['url']);
unlink($dest);
}
/**
* Write to invalid path.
*/
public function testWriteInvalidBlank()
{
$this->expectException(\Shaarli\Exceptions\IOException::class);
$conf = array('conf' => 'value');
@$this->configIO->write('', $conf);
}
}

View file

@ -1,197 +0,0 @@
<?php
namespace Shaarli\Config;
/**
* Unit tests for Class ConfigManagerTest
*
* Note: it only test the manager with ConfigJson,
* ConfigPhp is only a workaround to handle the transition to JSON type.
*/
class ConfigManagerTest extends \Shaarli\TestCase
{
/**
* @var ConfigManager
*/
protected $conf;
protected function setUp(): void
{
$this->conf = new ConfigManager('tests/utils/config/configJson');
}
/**
* Simple config test:
* 1. Set settings.
* 2. Check settings value.
*/
public function testSetGet()
{
$this->conf->set('paramInt', 42);
$this->conf->set('paramString', 'value1');
$this->conf->set('paramBool', false);
$this->conf->set('paramArray', ['foo' => 'bar']);
$this->conf->set('paramNull', null);
$this->assertEquals(42, $this->conf->get('paramInt'));
$this->assertEquals('value1', $this->conf->get('paramString'));
$this->assertFalse($this->conf->get('paramBool'));
$this->assertEquals(['foo' => 'bar'], $this->conf->get('paramArray'));
$this->assertEquals(null, $this->conf->get('paramNull'));
}
/**
* Set/write/get config test:
* 1. Set settings.
* 2. Write it to the config file.
* 3. Read the file.
* 4. Check settings value.
*/
public function testSetWriteGet()
{
$this->conf->set('paramInt', 42);
$this->conf->set('paramString', 'value1');
$this->conf->set('paramBool', false);
$this->conf->set('paramArray', ['foo' => 'bar']);
$this->conf->set('paramNull', null);
$this->conf->setConfigFile('tests/utils/config/configTmp');
$this->conf->write(true);
$this->conf->reload();
unlink($this->conf->getConfigFileExt());
$this->assertEquals(42, $this->conf->get('paramInt'));
$this->assertEquals('value1', $this->conf->get('paramString'));
$this->assertFalse($this->conf->get('paramBool'));
$this->assertEquals(['foo' => 'bar'], $this->conf->get('paramArray'));
$this->assertEquals(null, $this->conf->get('paramNull'));
}
/**
* Test set/write/get with nested keys.
*/
public function testSetWriteGetNested()
{
$this->conf->set('foo.bar.key.stuff', 'testSetWriteGetNested');
$this->conf->setConfigFile('tests/utils/config/configTmp');
$this->conf->write(true);
$this->conf->reload();
unlink($this->conf->getConfigFileExt());
$this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff'));
}
public function testSetDeleteNested()
{
$this->conf->set('foo.bar.key.stuff', 'testSetDeleteNested');
$this->assertTrue($this->conf->exists('foo.bar'));
$this->assertTrue($this->conf->exists('foo.bar.key.stuff'));
$this->assertEquals('testSetDeleteNested', $this->conf->get('foo.bar.key.stuff'));
$this->conf->remove('foo.bar');
$this->assertFalse($this->conf->exists('foo.bar.key.stuff'));
$this->assertFalse($this->conf->exists('foo.bar'));
}
/**
* Set with an empty key.
*/
public function testSetEmptyKey()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
$this->conf->set('', 'stuff');
}
/**
* Set with an array key.
*/
public function testSetArrayKey()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
$this->conf->set(['foo' => 'bar'], 'stuff');
}
/**
* Remove with an empty key.
*/
public function testRmoveEmptyKey()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
$this->conf->remove('');
}
/**
* Try to write the config without mandatory parameter (e.g. 'login').
*/
public function testWriteMissingParameter()
{
$this->expectException(\Shaarli\Config\Exception\MissingFieldConfigException::class);
$this->conf->setConfigFile('tests/utils/config/configTmp');
$this->assertFalse(file_exists($this->conf->getConfigFileExt()));
$this->conf->reload();
$this->conf->write(true);
}
/**
* Try to get non existent config keys.
*/
public function testGetNonExistent()
{
$this->assertEquals('', $this->conf->get('nope.test'));
$this->assertEquals('default', $this->conf->get('nope.test', 'default'));
}
/**
* Test the 'exists' method with existent values.
*/
public function testExistsOk()
{
$this->assertTrue($this->conf->exists('credentials.login'));
$this->assertTrue($this->conf->exists('config.foo'));
}
/**
* Test the 'exists' method with non existent or invalid values.
*/
public function testExistsKo()
{
$this->assertFalse($this->conf->exists('nope'));
$this->assertFalse($this->conf->exists('nope.nope'));
$this->assertFalse($this->conf->exists(''));
$this->assertFalse($this->conf->exists(false));
}
/**
* Reset the ConfigManager instance.
*/
public function testReset()
{
$confIO = $this->conf->getConfigIO();
$this->conf->reset();
$this->assertFalse($confIO === $this->conf->getConfigIO());
}
/**
* Reload the config from file.
*/
public function testReload()
{
$this->conf->setConfigFile('tests/utils/config/configTmp');
$newConf = ConfigJson::getPhpHeaders() . '{ "key": "value" }';
file_put_contents($this->conf->getConfigFileExt(), $newConf);
$this->conf->reload();
unlink($this->conf->getConfigFileExt());
// Previous conf no longer exists, and new values have been loaded.
$this->assertFalse($this->conf->exists('credentials.login'));
$this->assertEquals('value', $this->conf->get('key'));
}
}

View file

@ -1,100 +0,0 @@
<?php
namespace Shaarli\Config;
/**
* Class ConfigPhpTest
*
* We run tests in separate processes due to the usage for $GLOBALS
* which are kept between tests.
* @runTestsInSeparateProcesses
*/
class ConfigPhpTest extends \Shaarli\TestCase
{
/**
* @var ConfigPhp
*/
protected $configIO;
protected function setUp(): void
{
$this->configIO = new ConfigPhp();
}
/**
* Read a simple existing config file.
*/
public function testRead()
{
$conf = $this->configIO->read('tests/utils/config/configPhp.php');
$this->assertEquals('root', $conf['login']);
$this->assertEquals('lala', $conf['redirector']);
$this->assertEquals('data/datastore.php', $conf['config']['DATASTORE']);
$this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']);
}
/**
* Read a non existent config file -> empty array.
*/
public function testReadNonExistent()
{
$this->assertEquals([], $this->configIO->read('nope'));
}
/**
* Read an empty existent config file -> array with blank default values.
*/
public function testReadEmpty()
{
$dataFile = 'tests/utils/config/emptyConfigPhp.php';
$conf = $this->configIO->read($dataFile);
$this->assertEmpty($conf['login']);
$this->assertEmpty($conf['title']);
$this->assertEmpty($conf['titleLink']);
$this->assertEmpty($conf['config']);
$this->assertEmpty($conf['plugins']);
}
/**
* Write a new config file.
*/
public function testWriteNew()
{
$dataFile = 'tests/utils/config/configWrite.php';
$data = [
'login' => 'root',
'redirector' => 'lala',
'config' => [
'DATASTORE' => 'data/datastore.php',
],
'plugins' => [
'WALLABAG_VERSION' => '1',
]
];
$this->configIO->write($dataFile, $data);
$expected = '<?php
$GLOBALS[\'login\'] = \'root\';
$GLOBALS[\'redirector\'] = \'lala\';
$GLOBALS[\'config\'][\'DATASTORE\'] = \'data/datastore.php\';
$GLOBALS[\'plugins\'][\'WALLABAG_VERSION\'] = \'1\';
';
$this->assertEquals($expected, file_get_contents($dataFile));
unlink($dataFile);
}
/**
* Overwrite an existing setting.
*/
public function testOverwrite()
{
$source = 'tests/utils/config/configPhp.php';
$dest = 'tests/utils/config/configOverwrite.php';
copy($source, $dest);
$conf = $this->configIO->read($dest);
$conf['redirector'] = 'blabla';
$this->configIO->write($dest, $conf);
$conf = $this->configIO->read($dest);
$this->assertEquals('blabla', $conf['redirector']);
unlink($dest);
}
}

View file

@ -1,133 +0,0 @@
<?php
namespace Shaarli\Config;
use Shaarli\Config\Exception\PluginConfigOrderException;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
/**
* Unitary tests for Shaarli config related functions
*/
class ConfigPluginTest extends TestCase
{
/**
* Test save_plugin_config with valid data.
*
* @throws PluginConfigOrderException
*/
public function testSavePluginConfigValid()
{
$data = [
'order_plugin1' => 2, // no plugin related
'plugin2' => 0, // new - at the end
'plugin3' => 0, // 2nd
'order_plugin3' => 8,
'plugin4' => 0, // 1st
'order_plugin4' => 5,
];
$expected = [
'plugin3',
'plugin4',
'plugin2',
];
mkdir($path = __DIR__ . '/folder');
PluginManager::$PLUGINS_PATH = $path;
array_map(function (string $plugin) use ($path) {
touch($path . '/' . $plugin);
}, $expected);
$out = save_plugin_config($data);
$this->assertEquals($expected, $out);
array_map(function (string $plugin) use ($path) {
unlink($path . '/' . $plugin);
}, $expected);
rmdir($path);
}
/**
* Test save_plugin_config with invalid data.
*/
public function testSavePluginConfigInvalid()
{
$this->expectException(PluginConfigOrderException::class);
$data = [
'plugin2' => 0,
'plugin3' => 0,
'order_plugin3' => 0,
'plugin4' => 0,
'order_plugin4' => 0,
];
save_plugin_config($data);
}
/**
* Test save_plugin_config without data.
*/
public function testSavePluginConfigEmpty()
{
$this->assertEquals([], save_plugin_config([]));
}
/**
* Test validate_plugin_order with valid data.
*/
public function testValidatePluginOrderValid()
{
$data = [
'order_plugin1' => 2,
'plugin2' => 0,
'plugin3' => 0,
'order_plugin3' => 1,
'plugin4' => 0,
'order_plugin4' => 5,
];
$this->assertTrue(validate_plugin_order($data));
}
/**
* Test validate_plugin_order with invalid data.
*/
public function testValidatePluginOrderInvalid()
{
$data = [
'order_plugin1' => 2,
'order_plugin3' => 1,
'order_plugin4' => 1,
];
$this->assertFalse(validate_plugin_order($data));
}
/**
* Test load_plugin_parameter_values.
*/
public function testLoadPluginParameterValues()
{
$plugins = [
'plugin_name' => [
'parameters' => [
'param1' => ['value' => true],
'param2' => ['value' => false],
'param3' => ['value' => ''],
]
]
];
$parameters = [
'param1' => 'value1',
'param2' => 'value2',
];
$result = load_plugin_parameter_values($plugins, $parameters);
$this->assertEquals('value1', $result['plugin_name']['parameters']['param1']['value']);
$this->assertEquals('value2', $result['plugin_name']['parameters']['param2']['value']);
$this->assertEquals('', $result['plugin_name']['parameters']['param3']['value']);
}
}

View file

@ -1,98 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Container;
use Psr\Log\LoggerInterface;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\Front\Controller\Visitor\ErrorController;
use Shaarli\Front\Controller\Visitor\ErrorNotFoundController;
use Shaarli\History;
use Shaarli\Http\HttpAccess;
use Shaarli\Http\MetadataRetriever;
use Shaarli\Netscape\NetscapeBookmarkUtils;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Security\CookieManager;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Shaarli\Updater\Updater;
use Slim\Http\Environment;
class ContainerBuilderTest extends TestCase
{
/** @var ConfigManager */
protected $conf;
/** @var SessionManager */
protected $sessionManager;
/** @var LoginManager */
protected $loginManager;
/** @var ContainerBuilder */
protected $containerBuilder;
/** @var CookieManager */
protected $cookieManager;
/** @var PluginManager */
protected $pluginManager;
public function setUp(): void
{
$this->conf = new ConfigManager('tests/utils/config/configJson');
$this->sessionManager = $this->createMock(SessionManager::class);
$this->cookieManager = $this->createMock(CookieManager::class);
$this->pluginManager = $this->createMock(PluginManager::class);
$this->loginManager = $this->createMock(LoginManager::class);
$this->loginManager->method('isLoggedIn')->willReturn(true);
$this->containerBuilder = new ContainerBuilder(
$this->conf,
$this->sessionManager,
$this->cookieManager,
$this->loginManager,
$this->pluginManager,
$this->createMock(LoggerInterface::class)
);
}
public function testBuildContainer(): void
{
$container = $this->containerBuilder->build();
static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
static::assertInstanceOf(CookieManager::class, $container->cookieManager);
static::assertInstanceOf(ConfigManager::class, $container->conf);
static::assertInstanceOf(ErrorController::class, $container->errorHandler);
static::assertInstanceOf(Environment::class, $container->environment);
static::assertInstanceOf(FeedBuilder::class, $container->feedBuilder);
static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
static::assertInstanceOf(History::class, $container->history);
static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
static::assertInstanceOf(LoginManager::class, $container->loginManager);
static::assertInstanceOf(LoggerInterface::class, $container->logger);
static::assertInstanceOf(MetadataRetriever::class, $container->metadataRetriever);
static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils);
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler);
static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler);
static::assertInstanceOf(PluginManager::class, $container->pluginManager);
static::assertInstanceOf(SessionManager::class, $container->sessionManager);
static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
static::assertInstanceOf(Updater::class, $container->updater);
// Set by the middleware
static::assertNull($container->basePath);
}
}

View file

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Container;
use PHPUnit\Framework\MockObject\MockObject;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\History;
use Shaarli\Http\HttpAccess;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
/**
* Test helper allowing auto-completion for MockObjects.
*
* @property mixed[] $environment $_SERVER automatically injected by Slim
* @property MockObject|ConfigManager $conf
* @property MockObject|SessionManager $sessionManager
* @property MockObject|LoginManager $loginManager
* @property MockObject|string $webPath
* @property MockObject|History $history
* @property MockObject|BookmarkServiceInterface $bookmarkService
* @property MockObject|PageBuilder $pageBuilder
* @property MockObject|PluginManager $pluginManager
* @property MockObject|FormatterFactory $formatterFactory
* @property MockObject|PageCacheManager $pageCacheManager
* @property MockObject|FeedBuilder $feedBuilder
* @property MockObject|Thumbnailer $thumbnailer
* @property MockObject|HttpAccess $httpAccess
*/
class ShaarliTestContainer extends ShaarliContainer
{
}

View file

@ -1,36 +0,0 @@
FROM alpine:3.16
MAINTAINER Shaarli Community
RUN apk --update --no-cache add \
ca-certificates \
curl \
make \
php8 \
php8-ctype \
php8-curl \
php8-dom \
php8-gd \
php8-gettext \
php8-iconv \
php8-intl \
php8-json \
php8-mbstring \
php8-openssl \
php8-phar \
php8-session \
php8-simplexml \
php8-tokenizer \
php8-xdebug \
php8-xmlwriter \
php8-xml \
php8-zlib \
rsync
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN mkdir /shaarli
WORKDIR /shaarli
VOLUME /shaarli
ENTRYPOINT ["make"]
CMD []

View file

@ -1,35 +0,0 @@
FROM debian:jessie
MAINTAINER Shaarli Community
ENV TERM dumb
ENV DEBIAN_FRONTEND noninteractive
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ca-certificates \
curl \
locales \
make \
php5 \
php5-curl \
php5-gd \
php5-intl \
php5-xdebug \
rsync \
&& apt-get clean
RUN locale-gen en_US.UTF-8 \
&& locale-gen de_DE.UTF-8 \
&& locale-gen fr_FR.UTF-8
ADD https://getcomposer.org/composer.phar /usr/local/bin/composer
RUN chmod 755 /usr/local/bin/composer
RUN mkdir /shaarli
WORKDIR /shaarli
VOLUME /shaarli
ENTRYPOINT ["make"]
CMD []

View file

@ -1,36 +0,0 @@
FROM debian:stretch
MAINTAINER Shaarli Community
ENV TERM dumb
ENV DEBIAN_FRONTEND noninteractive
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ca-certificates \
curl \
locales \
make \
php7.0 \
php7.0-curl \
php7.0-gd \
php7.0-intl \
php7.0-xml \
php-xdebug \
rsync \
&& apt-get clean
RUN locale-gen en_US.UTF-8 \
&& locale-gen de_DE.UTF-8 \
&& locale-gen fr_FR.UTF-8
ADD https://getcomposer.org/composer.phar /usr/local/bin/composer
RUN chmod 755 /usr/local/bin/composer
RUN mkdir /shaarli
WORKDIR /shaarli
VOLUME /shaarli
ENTRYPOINT ["make"]
CMD []

View file

@ -1,36 +0,0 @@
FROM ubuntu:16.04
MAINTAINER Shaarli Community
ENV TERM dumb
ENV DEBIAN_FRONTEND noninteractive
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ca-certificates \
curl \
language-pack-de \
language-pack-en \
language-pack-fr \
locales \
make \
php7.0 \
php7.0-curl \
php7.0-gd \
php7.0-intl \
php7.0-xml \
php-xdebug \
rsync \
&& apt-get clean
ADD https://getcomposer.org/composer.phar /usr/local/bin/composer
RUN chmod 755 /usr/local/bin/composer
RUN useradd -m dev \
&& mkdir /shaarli
USER dev
WORKDIR /shaarli
ENTRYPOINT ["make"]
CMD []

View file

@ -1,162 +0,0 @@
<?php
/**
* PageCache tests
*/
namespace Shaarli\Feed;
/**
* Unitary tests for cached pages
*/
class CachedPageTest extends \Shaarli\TestCase
{
// test cache directory
protected static $testCacheDir = 'sandbox/pagecache';
protected static $url = 'http://shaar.li/feed/atom';
protected static $filename;
/**
* Create the cache directory if needed
*/
public static function setUpBeforeClass(): void
{
if (!is_dir(self::$testCacheDir)) {
mkdir(self::$testCacheDir);
}
self::$filename = self::$testCacheDir . '/' . sha1(self::$url) . '.cache';
}
/**
* Reset the page cache
*/
protected function setUp(): void
{
if (file_exists(self::$filename)) {
unlink(self::$filename);
}
}
/**
* Create a new cached page
*/
public function testConstruct()
{
new CachedPage(self::$testCacheDir, '', true, null);
new CachedPage(self::$testCacheDir, '', false, null);
new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/rss', true, null);
new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/atom', false, null);
$this->addToAssertionCount(1);
}
/**
* Cache a page's content
*/
public function testCache()
{
$page = new CachedPage(self::$testCacheDir, self::$url, true, null);
$this->assertFileNotExists(self::$filename);
$page->cache('<p>Some content</p>');
$this->assertFileExists(self::$filename);
$this->assertEquals(
'<p>Some content</p>',
file_get_contents(self::$filename)
);
}
/**
* "Cache" a page's content - the page is not to be cached
*/
public function testShouldNotCache()
{
$page = new CachedPage(self::$testCacheDir, self::$url, false, null);
$this->assertFileNotExists(self::$filename);
$page->cache('<p>Some content</p>');
$this->assertFileNotExists(self::$filename);
}
/**
* Return a page's cached content
*/
public function testCachedVersion()
{
$page = new CachedPage(self::$testCacheDir, self::$url, true, null);
$this->assertFileNotExists(self::$filename);
$page->cache('<p>Some content</p>');
$this->assertFileExists(self::$filename);
$this->assertEquals(
'<p>Some content</p>',
$page->cachedVersion()
);
}
/**
* Return a page's cached content - the file does not exist
*/
public function testCachedVersionNoFile()
{
$page = new CachedPage(self::$testCacheDir, self::$url, true, null);
$this->assertFileNotExists(self::$filename);
$this->assertEquals(
null,
$page->cachedVersion()
);
}
/**
* Return a page's cached content - the page is not to be cached
*/
public function testNoCachedVersion()
{
$page = new CachedPage(self::$testCacheDir, self::$url, false, null);
$this->assertFileNotExists(self::$filename);
$this->assertEquals(
null,
$page->cachedVersion()
);
}
/**
* Return a page's cached content within date period
*/
public function testCachedVersionInDatePeriod()
{
$period = new \DatePeriod(
new \DateTime('yesterday'),
new \DateInterval('P1D'),
new \DateTime('tomorrow')
);
$page = new CachedPage(self::$testCacheDir, self::$url, true, $period);
$this->assertFileNotExists(self::$filename);
$page->cache('<p>Some content</p>');
$this->assertFileExists(self::$filename);
$this->assertEquals(
'<p>Some content</p>',
$page->cachedVersion()
);
}
/**
* Return a page's cached content outside of date period
*/
public function testCachedVersionNotInDatePeriod()
{
$period = new \DatePeriod(
new \DateTime('yesterday noon'),
new \DateInterval('P1D'),
new \DateTime('yesterday midnight')
);
$page = new CachedPage(self::$testCacheDir, self::$url, true, $period);
$this->assertFileNotExists(self::$filename);
$page->cache('<p>Some content</p>');
$this->assertFileExists(self::$filename);
$this->assertNull($page->cachedVersion());
}
}

View file

@ -1,304 +0,0 @@
<?php
namespace Shaarli\Feed;
use DateTime;
use malkusch\lock\mutex\NoMutex;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\LinkDB;
use Shaarli\Config\ConfigManager;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Shaarli\TestCase;
use Shaarli\Tests\Utils\ReferenceLinkDB;
/**
* FeedBuilderTest class.
*
* Unit tests for FeedBuilder.
*/
class FeedBuilderTest extends TestCase
{
/**
* @var string locale Basque (Spain).
*/
public static $LOCALE = 'eu_ES';
/**
* @var string language in RSS format.
*/
public static $RSS_LANGUAGE = 'eu-es';
/**
* @var string language in ATOM format.
*/
public static $ATOM_LANGUAGUE = 'eu';
protected static $testDatastore = 'sandbox/datastore.php';
public static $bookmarkService;
public static $formatter;
public static $serverInfo;
/**
* Called before every test method.
*/
public static function setUpBeforeClass(): void
{
$mutex = new NoMutex();
$conf = new ConfigManager('tests/utils/config/configJson');
$conf->set('resource.datastore', self::$testDatastore);
$refLinkDB = new ReferenceLinkDB();
$refLinkDB->write(self::$testDatastore);
$history = new History('sandbox/history.php');
$factory = new FormatterFactory($conf, true);
$pluginManager = new PluginManager($conf);
self::$formatter = $factory->getFormatter();
self::$bookmarkService = new BookmarkFileService(
$conf,
$pluginManager,
$history,
$mutex,
true
);
self::$serverInfo = [
'HTTPS' => 'Off',
'SERVER_NAME' => 'host.tld',
'SERVER_PORT' => '80',
'SCRIPT_NAME' => '/index.php',
'REQUEST_URI' => '/feed/atom',
];
}
/**
* Test buildData with RSS feed.
*/
public function testRSSBuildData()
{
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_RSS, null);
// Test headers (RSS)
$this->assertEquals(self::$RSS_LANGUAGE, $data['language']);
$this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']);
$this->assertEquals(true, $data['show_dates']);
$this->assertEquals('http://host.tld/feed/atom', $data['self_link']);
$this->assertEquals('http://host.tld/', $data['index_url']);
$this->assertFalse($data['usepermalinks']);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
// Test first not pinned link (note link)
$link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(
DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
$link['created']
);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
$this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']);
$pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']);
$up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']);
$this->assertEquals($pub, $up);
$this->assertContainsPolyfill('Stallman has a beard', $link['description']);
$this->assertContainsPolyfill('Permalink', $link['description']);
$this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
$this->assertEquals(1, count($link['taglist']));
$this->assertEquals('sTuff', $link['taglist'][0]);
// Test URL with external link.
$this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links'][8]['url']);
// Test multitags.
$this->assertEquals(5, count($data['links'][6]['taglist']));
$this->assertEquals('css', $data['links'][6]['taglist'][0]);
// Test update date
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
}
/**
* Test buildData with ATOM feed (test only specific to ATOM).
*/
public function testAtomBuildData()
{
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
$link = $data['links'][array_keys($data['links'])[0]];
$this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
}
/**
* Test buildData with search criteria.
*/
public function testBuildDataFiltered()
{
$criteria = [
'searchtags' => 'stuff',
'searchterm' => 'beard',
];
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
$this->assertEquals(1, count($data['links']));
$link = array_shift($data['links']);
$this->assertEquals(41, $link['id']);
$this->assertEquals(
DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
$link['created']
);
}
/**
* Test buildData with nb limit.
*/
public function testBuildDataCount()
{
$criteria = [
'nb' => '3',
];
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
$this->assertEquals(3, count($data['links']));
$link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(
DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
$link['created']
);
}
/**
* Test buildData with permalinks on.
*/
public function testBuildDataPermalinks()
{
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$feedBuilder->setUsePermalinks(true);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertTrue($data['usepermalinks']);
// First link is a permalink
$link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(
DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
$link['created']
);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
$this->assertContainsPolyfill('Direct link', $link['description']);
$this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
// Second link is a direct link
$link = $data['links'][array_keys($data['links'])[1]];
$this->assertEquals(8, $link['id']);
$this->assertEquals(
DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'),
$link['created']
);
$this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']);
$this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']);
$this->assertContainsPolyfill('Direct link', $link['description']);
$this->assertContainsPolyfill('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']);
}
/**
* Test buildData with hide dates settings.
*/
public function testBuildDataHideDates()
{
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$feedBuilder->setHideDates(true);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertFalse($data['show_dates']);
// Show dates while logged in
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
static::$serverInfo,
true
);
$feedBuilder->setLocale(self::$LOCALE);
$feedBuilder->setHideDates(true);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertTrue($data['show_dates']);
}
/**
* Test buildData when Shaarli is served from a subdirectory
*/
public function testBuildDataServerSubdir()
{
$serverInfo = [
'HTTPS' => 'Off',
'SERVER_NAME' => 'host.tld',
'SERVER_PORT' => '8080',
'SCRIPT_NAME' => '/~user/shaarli/index.php',
'REQUEST_URI' => '/~user/shaarli/feed/atom',
];
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
self::$formatter,
$serverInfo,
false
);
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(
'http://host.tld:8080/~user/shaarli/feed/atom',
$data['self_link']
);
// Test first link (note link)
$link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']);
$this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']);
$this->assertContainsPolyfill('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
}
}

View file

@ -1,319 +0,0 @@
<?php
namespace Shaarli\Formatter;
use DateTime;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\TestCase;
/**
* Class BookmarkDefaultFormatterTest
* @package Shaarli\Formatter
*/
class BookmarkDefaultFormatterTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var BookmarkFormatter */
protected $formatter;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize formatter instance.
*/
protected function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->formatter = new BookmarkDefaultFormatter($this->conf, true);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatFull()
{
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setShortUrl($short = 'abcdef');
$bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
$bookmark->setDescription($desc = '<h2>Content</h2><p>`Here is some content</p>');
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
$bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
$bookmark->setSticky(true);
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
$bookmark->setPrivate(true);
$link = $this->formatter->format($bookmark);
$this->assertEquals($id, $link['id']);
$this->assertEquals($short, $link['shorturl']);
$this->assertEquals('https://sub.domain.tld?query=here&amp;for=real#hash', $link['url']);
$this->assertEquals(
'https://sub.domain.tld?query=here&amp;for=real#hash',
$link['real_url']
);
$this->assertEquals('This is a &lt;strong&gt;bookmark&lt;/strong&gt;', $link['title']);
$this->assertEquals(
'&lt;h2&gt;Content&lt;/h2&gt;&lt;p&gt;`Here is some content&lt;/p&gt;',
$link['description']
);
$tags[3] = '&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;';
$this->assertEquals($tags, $link['taglist']);
$this->assertEquals(implode(' ', $tags), $link['tags']);
$this->assertEquals(
'http://domain2.tdl2/?type=img&amp;name=file.png',
$link['thumbnail']
);
$this->assertEquals($created, $link['created']);
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
$this->assertEquals($updated, $link['updated']);
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
$this->assertTrue($link['private']);
$this->assertTrue($link['sticky']);
$this->assertEquals('private', $link['class']);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatMinimal()
{
$bookmark = new Bookmark();
$link = $this->formatter->format($bookmark);
$this->assertEmpty($link['id']);
$this->assertEmpty($link['shorturl']);
$this->assertEmpty($link['url']);
$this->assertEmpty($link['real_url']);
$this->assertEmpty($link['title']);
$this->assertEmpty($link['description']);
$this->assertEmpty($link['taglist']);
$this->assertEmpty($link['tags']);
$this->assertEmpty($link['thumbnail']);
$this->assertEmpty($link['created']);
$this->assertEmpty($link['timestamp']);
$this->assertEmpty($link['updated']);
$this->assertEmpty($link['updated_timestamp']);
$this->assertFalse($link['private']);
$this->assertFalse($link['sticky']);
$this->assertEmpty($link['class']);
}
/**
* Make sure that the description is properly formatted by the default formatter.
*/
public function testFormatDescription()
{
$description = [];
$description[] = 'This a <strong>description</strong>' . PHP_EOL;
$description[] = 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL;
$description[] = 'Also, there is an #hashtag added' . PHP_EOL;
$description[] = ' A N D KEEP SPACES ! ' . PHP_EOL;
$bookmark = new Bookmark();
$bookmark->setDescription(implode('', $description));
$link = $this->formatter->format($bookmark);
$description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />';
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$description[1] = 'text <a href="' . $url . '">' . $url . '</a> more text<br />';
$description[2] = 'Also, there is an <a href="./add-tag/hashtag" ' .
'title="Hashtag hashtag">#hashtag</a> added<br />';
$description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; ' .
'SPACES &nbsp; &nbsp;! &nbsp; <br />';
$this->assertEquals(implode(PHP_EOL, $description) . PHP_EOL, $link['description']);
}
/**
* Test formatting URL with an index_url set
* It should prepend relative links.
*/
public function testFormatNoteWithIndexUrl()
{
$bookmark = new Bookmark();
$bookmark->setUrl($short = '?abcdef');
$description = 'Text #hashtag more text';
$bookmark->setDescription($description);
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
$link = $this->formatter->format($bookmark);
$this->assertEquals($root . $short, $link['url']);
$this->assertEquals($root . $short, $link['real_url']);
$this->assertEquals(
'Text <a href="' . $root . './add-tag/hashtag" title="Hashtag hashtag">' .
'#hashtag</a> more text',
$link['description']
);
}
/**
* Make sure that private tags are properly filtered out when the user is logged out.
*/
public function testFormatTagListRemovePrivate(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setTags($tags = ['bookmark', '.private', 'othertag']);
$link = $this->formatter->format($bookmark);
unset($tags[1]);
$tags = array_values($tags);
$this->assertSame(11, $link['id']);
$this->assertSame($tags, $link['taglist']);
$this->assertSame(implode(' ', $tags), $link['tags']);
}
/**
* Test formatTitleHtml with search result highlight.
*/
public function testFormatTitleHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setTitle('PSR-2: Coding Style Guide');
$bookmark->setAdditionalContentEntry(
'search_highlight',
['title' => [
['start' => 0, 'end' => 5], // "psr-2"
['start' => 7, 'end' => 13], // coding
['start' => 20, 'end' => 25], // guide
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">PSR-2</span>: ' .
'<span class="search-highlight">Coding</span> Style ' .
'<span class="search-highlight">Guide</span>',
$link['title_html']
);
}
/**
* Test formatDescription with search result highlight.
*/
public function testFormatDescriptionWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setDescription(
'This guide extends and expands on PSR-1, the basic coding standard.' . PHP_EOL .
'https://www.php-fig.org/psr/psr-1/'
);
$bookmark->setAdditionalContentEntry(
'search_highlight',
['description' => [
['start' => 0, 'end' => 10], // "This guide"
['start' => 45, 'end' => 50], // basic
['start' => 58, 'end' => 67], // standard.
['start' => 84, 'end' => 87], // fig
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">This guide</span> extends and expands on PSR-1, the ' .
'<span class="search-highlight">basic</span> coding ' .
'<span class="search-highlight">standard.</span><br />' . PHP_EOL .
'<a href="https://www.php-fig.org/psr/psr-1/">' .
'https://www.php-<span class="search-highlight">fig</span>.org/psr/psr-1/' .
'</a>',
$link['description']
);
}
/**
* Test formatUrlHtml with search result highlight.
*/
public function testFormatUrlHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setUrl('http://www.php-fig.org/psr/psr-2/');
$bookmark->setAdditionalContentEntry(
'search_highlight',
['url' => [
['start' => 0, 'end' => 4], // http
['start' => 15, 'end' => 18], // fig
['start' => 27, 'end' => 33], // "psr-2/"
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">http</span>://www.php-' .
'<span class="search-highlight">fig</span>.org/psr/' .
'<span class="search-highlight">psr-2/</span>',
$link['url_html']
);
}
/**
* Test formatTagListHtml with search result highlight.
*/
public function testFormatTagListHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setTagsString('coding-style standards quality assurance');
$bookmark->setAdditionalContentEntry(
'search_highlight',
['tags' => [
['start' => 0, 'end' => 12], // coding-style
['start' => 23, 'end' => 30], // quality
['start' => 31, 'end' => 40], // assurance
],]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
[
'<span class="search-highlight">coding-style</span>',
'standards',
'<span class="search-highlight">quality</span>',
'<span class="search-highlight">assurance</span>',
],
$link['taglist_html']
);
}
/**
* Test default formatting with formatter_settings.autolink set to false:
* URLs and hashtags should not be transformed
*/
public function testFormatDescriptionWithoutLinkification(): void
{
$this->conf->set('formatter_settings.autolink', false);
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setDescription('Hi!' . PHP_EOL . 'https://thisisaurl.tld #hashtag');
$link = $this->formatter->format($bookmark);
static::assertSame(
'Hi!<br />' . PHP_EOL . 'https://thisisaurl.tld &nbsp;#hashtag',
$link['description']
);
}
}

View file

@ -1,162 +0,0 @@
<?php
namespace Shaarli\Formatter;
use DateTime;
use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
/**
* Class BookmarkMarkdownExtraFormatterTest
* @package Shaarli\Formatter
*/
class BookmarkMarkdownExtraFormatterTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var BookmarkFormatter */
protected $formatter;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize formatter instance.
*/
public function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->formatter = new BookmarkMarkdownExtraFormatter($this->conf, true);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatExtra(): void
{
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setShortUrl($short = 'abcdef');
$bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
$bookmark->setDescription('<h2>Content</h2><p>`Here is some content</p>');
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
$bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
$bookmark->setSticky(true);
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
$bookmark->setPrivate(true);
$link = $this->formatter->format($bookmark);
$this->assertEquals($id, $link['id']);
$this->assertEquals($short, $link['shorturl']);
$this->assertEquals('https://sub.domain.tld?query=here&amp;for=real#hash', $link['url']);
$this->assertEquals(
'https://sub.domain.tld?query=here&amp;for=real#hash',
$link['real_url']
);
$this->assertEquals('This is a &lt;strong&gt;bookmark&lt;/strong&gt;', $link['title']);
$this->assertEquals(
'<div class="markdown"><p>' .
'&lt;h2&gt;Content&lt;/h2&gt;&lt;p&gt;`Here is some content&lt;/p&gt;' .
'</p></div>',
$link['description']
);
$tags[3] = '&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;';
$this->assertEquals($tags, $link['taglist']);
$this->assertEquals(implode(' ', $tags), $link['tags']);
$this->assertEquals(
'http://domain2.tdl2/?type=img&amp;name=file.png',
$link['thumbnail']
);
$this->assertEquals($created, $link['created']);
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
$this->assertEquals($updated, $link['updated']);
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
$this->assertTrue($link['private']);
$this->assertTrue($link['sticky']);
$this->assertEquals('private', $link['class']);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatExtraMinimal(): void
{
$bookmark = new Bookmark();
$link = $this->formatter->format($bookmark);
$this->assertEmpty($link['id']);
$this->assertEmpty($link['shorturl']);
$this->assertEmpty($link['url']);
$this->assertEmpty($link['real_url']);
$this->assertEmpty($link['title']);
$this->assertEmpty($link['description']);
$this->assertEmpty($link['taglist']);
$this->assertEmpty($link['tags']);
$this->assertEmpty($link['thumbnail']);
$this->assertEmpty($link['created']);
$this->assertEmpty($link['timestamp']);
$this->assertEmpty($link['updated']);
$this->assertEmpty($link['updated_timestamp']);
$this->assertFalse($link['private']);
$this->assertFalse($link['sticky']);
$this->assertEmpty($link['class']);
}
/**
* Make sure that the description is properly formatted by the default formatter.
*/
public function testFormatExtrraDescription(): void
{
$description = 'This a <strong>description</strong>' . PHP_EOL;
$description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL;
$description .= 'Also, there is an #hashtag added' . PHP_EOL;
$description .= ' A N D KEEP SPACES ! ' . PHP_EOL;
$description .= '# Header {.class}' . PHP_EOL;
$bookmark = new Bookmark();
$bookmark->setDescription($description);
$link = $this->formatter->format($bookmark);
$description = '<div class="markdown"><p>';
$description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />' . PHP_EOL;
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$description .= 'text <a href="' . $url . '">' . $url . '</a> more text<br />' . PHP_EOL;
$description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />' . PHP_EOL;
$description .= 'A N D KEEP SPACES ! </p>' . PHP_EOL;
$description .= '<h1 class="class">Header</h1>';
$description .= '</div>';
$this->assertEquals($description, $link['description']);
}
/**
* Test formatting URL with an index_url set
* It should prepend relative links.
*/
public function testFormatExtraNoteWithIndexUrl(): void
{
$bookmark = new Bookmark();
$bookmark->setUrl($short = '?abcdef');
$description = 'Text #hashtag more text';
$bookmark->setDescription($description);
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
$description = '<div class="markdown"><p>';
$description .= 'Text <a href="' . $root . './add-tag/hashtag">#hashtag</a> more text';
$description .= '</p></div>';
$link = $this->formatter->format($bookmark);
$this->assertEquals($root . $short, $link['url']);
$this->assertEquals($root . $short, $link['real_url']);
$this->assertEquals(
$description,
$link['description']
);
}
}

View file

@ -1,203 +0,0 @@
<?php
namespace Shaarli\Formatter;
use DateTime;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\TestCase;
/**
* Class BookmarkMarkdownFormatterTest
* @package Shaarli\Formatter
*/
class BookmarkMarkdownFormatterTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var BookmarkFormatter */
protected $formatter;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize formatter instance.
*/
protected function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->formatter = new BookmarkMarkdownFormatter($this->conf, true);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatFull()
{
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setShortUrl($short = 'abcdef');
$bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
$bookmark->setDescription('<h2>Content</h2><p>`Here is some content</p>');
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
$bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
$bookmark->setSticky(true);
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
$bookmark->setPrivate(true);
$link = $this->formatter->format($bookmark);
$this->assertEquals($id, $link['id']);
$this->assertEquals($short, $link['shorturl']);
$this->assertEquals('https://sub.domain.tld?query=here&amp;for=real#hash', $link['url']);
$this->assertEquals(
'https://sub.domain.tld?query=here&amp;for=real#hash',
$link['real_url']
);
$this->assertEquals('This is a &lt;strong&gt;bookmark&lt;/strong&gt;', $link['title']);
$this->assertEquals(
'<div class="markdown"><p>' .
'&lt;h2&gt;Content&lt;/h2&gt;&lt;p&gt;`Here is some content&lt;/p&gt;' .
'</p></div>',
$link['description']
);
$tags[3] = '&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;';
$this->assertEquals($tags, $link['taglist']);
$this->assertEquals(implode(' ', $tags), $link['tags']);
$this->assertEquals(
'http://domain2.tdl2/?type=img&amp;name=file.png',
$link['thumbnail']
);
$this->assertEquals($created, $link['created']);
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
$this->assertEquals($updated, $link['updated']);
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
$this->assertTrue($link['private']);
$this->assertTrue($link['sticky']);
$this->assertEquals('private', $link['class']);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatMinimal()
{
$bookmark = new Bookmark();
$link = $this->formatter->format($bookmark);
$this->assertEmpty($link['id']);
$this->assertEmpty($link['shorturl']);
$this->assertEmpty($link['url']);
$this->assertEmpty($link['real_url']);
$this->assertEmpty($link['title']);
$this->assertEmpty($link['description']);
$this->assertEmpty($link['taglist']);
$this->assertEmpty($link['tags']);
$this->assertEmpty($link['thumbnail']);
$this->assertEmpty($link['created']);
$this->assertEmpty($link['timestamp']);
$this->assertEmpty($link['updated']);
$this->assertEmpty($link['updated_timestamp']);
$this->assertFalse($link['private']);
$this->assertFalse($link['sticky']);
$this->assertEmpty($link['class']);
}
/**
* Make sure that the description is properly formatted by the default formatter.
*/
public function testFormatDescription()
{
$description = 'This a <strong>description</strong>' . PHP_EOL;
$description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL;
$description .= 'Also, there is an #hashtag added' . PHP_EOL;
$description .= ' A N D KEEP SPACES ! ' . PHP_EOL;
$bookmark = new Bookmark();
$bookmark->setDescription($description);
$link = $this->formatter->format($bookmark);
$description = '<div class="markdown"><p>';
$description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />' . PHP_EOL;
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$description .= 'text <a href="' . $url . '">' . $url . '</a> more text<br />' . PHP_EOL;
$description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />' . PHP_EOL;
$description .= 'A N D KEEP SPACES ! ';
$description .= '</p></div>';
$this->assertEquals($description, $link['description']);
}
/**
* Make sure that the description is properly formatted by the default formatter.
*/
public function testFormatDescriptionWithSearchHighlight()
{
$description = 'This a <strong>description</strong>' . PHP_EOL;
$description .= 'text https://sub.domain.tld?query=here&for=real#hash more text' . PHP_EOL;
$description .= 'Also, there is an #hashtag added' . PHP_EOL;
$description .= ' A N D KEEP SPACES ! ' . PHP_EOL;
$description .= 'And [yet another link](https://other.domain.tld)' . PHP_EOL;
$bookmark = new Bookmark();
$bookmark->setDescription($description);
$bookmark->setAdditionalContentEntry(
'search_highlight',
['description' => [
['start' => 18, 'end' => 26], // cription
['start' => 49, 'end' => 52], // sub
['start' => 84, 'end' => 88], // hash
['start' => 118, 'end' => 123], // hasht
['start' => 203, 'end' => 215], // other.domain
]]
);
$link = $this->formatter->format($bookmark);
$description = '<div class="markdown"><p>';
$description .= 'This a &lt;strong&gt;des<span class="search-highlight">cription</span>&lt;/strong&gt;<br />' .
PHP_EOL;
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$highlighted = 'https://<span class="search-highlight">sub</span>.domain.tld';
$highlighted .= '?query=here&amp;for=real#<span class="search-highlight">hash</span>';
$description .= 'text <a href="' . $url . '">' . $highlighted . '</a> more text<br />' . PHP_EOL;
$description .= 'Also, there is an <a href="./add-tag/hashtag">#<span class="search-highlight">hasht</span>' .
'ag</a> added<br />' . PHP_EOL;
$description .= 'A N D KEEP SPACES !<br />' . PHP_EOL;
$description .= 'And <a href="https://other.domain.tld">' .
'<span class="search-highlight">yet another link</span></a>';
$description .= '</p></div>';
$this->assertEquals($description, $link['description']);
}
/**
* Test formatting URL with an index_url set
* It should prepend relative links.
*/
public function testFormatNoteWithIndexUrl()
{
$bookmark = new Bookmark();
$bookmark->setUrl($short = '?abcdef');
$description = 'Text #hashtag more text';
$bookmark->setDescription($description);
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
$description = '<div class="markdown"><p>';
$description .= 'Text <a href="' . $root . './add-tag/hashtag">#hashtag</a> more text';
$description .= '</p></div>';
$link = $this->formatter->format($bookmark);
$this->assertEquals($root . $short, $link['url']);
$this->assertEquals($root . $short, $link['real_url']);
$this->assertEquals(
$description,
$link['description']
);
}
}

View file

@ -1,97 +0,0 @@
<?php
namespace Shaarli\Formatter;
use DateTime;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\TestCase;
/**
* Class BookmarkRawFormatterTest
* @package Shaarli\Formatter
*/
class BookmarkRawFormatterTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var BookmarkFormatter */
protected $formatter;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize formatter instance.
*/
protected function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->formatter = new BookmarkRawFormatter($this->conf, true);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatFull()
{
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setShortUrl($short = 'abcdef');
$bookmark->setUrl($url = 'https://sub.domain.tld?query=here&for=real#hash');
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
$bookmark->setDescription($desc = '<h2>Content</h2><p>`Here is some content</p>');
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
$bookmark->setThumbnail($thumb = 'http://domain2.tdl2/file.png');
$bookmark->setSticky(true);
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
$bookmark->setPrivate(true);
$link = $this->formatter->format($bookmark);
$this->assertEquals($id, $link['id']);
$this->assertEquals($short, $link['shorturl']);
$this->assertEquals($url, $link['url']);
$this->assertEquals($url, $link['real_url']);
$this->assertEquals($title, $link['title']);
$this->assertEquals($desc, $link['description']);
$this->assertEquals($tags, $link['taglist']);
$this->assertEquals(implode(' ', $tags), $link['tags']);
$this->assertEquals($thumb, $link['thumbnail']);
$this->assertEquals($created, $link['created']);
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
$this->assertEquals($updated, $link['updated']);
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
$this->assertTrue($link['private']);
$this->assertTrue($link['sticky']);
$this->assertEquals('private', $link['class']);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatMinimal()
{
$bookmark = new Bookmark();
$link = $this->formatter->format($bookmark);
$this->assertEmpty($link['id']);
$this->assertEmpty($link['shorturl']);
$this->assertEmpty($link['url']);
$this->assertEmpty($link['real_url']);
$this->assertEmpty($link['title']);
$this->assertEmpty($link['description']);
$this->assertEmpty($link['taglist']);
$this->assertEmpty($link['tags']);
$this->assertEmpty($link['thumbnail']);
$this->assertEmpty($link['created']);
$this->assertEmpty($link['timestamp']);
$this->assertEmpty($link['updated']);
$this->assertEmpty($link['updated_timestamp']);
$this->assertFalse($link['private']);
$this->assertFalse($link['sticky']);
$this->assertEmpty($link['class']);
}
}

View file

@ -1,101 +0,0 @@
<?php
namespace Shaarli\Formatter;
use Shaarli\Config\ConfigManager;
use Shaarli\TestCase;
/**
* Class FormatterFactoryTest
*
* @package Shaarli\Formatter
*/
class FormatterFactoryTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var FormatterFactory instance */
protected $factory;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize FormatterFactory instance
*/
protected function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf . '.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->factory = new FormatterFactory($this->conf, true);
}
/**
* Test creating an instance of BookmarkFormatter without any setting -> default formatter
*/
public function testCreateInstanceDefault()
{
$this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter());
}
/**
* Test creating an instance of BookmarkDefaultFormatter from settings
*/
public function testCreateInstanceDefaultSetting()
{
$this->conf->set('formatter', 'default');
$this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter());
}
/**
* Test creating an instance of BookmarkDefaultFormatter from parameter
*/
public function testCreateInstanceDefaultParameter()
{
$this->assertInstanceOf(
BookmarkDefaultFormatter::class,
$this->factory->getFormatter('default')
);
}
/**
* Test creating an instance of BookmarkRawFormatter from settings
*/
public function testCreateInstanceRawSetting()
{
$this->conf->set('formatter', 'raw');
$this->assertInstanceOf(BookmarkRawFormatter::class, $this->factory->getFormatter());
}
/**
* Test creating an instance of BookmarkRawFormatter from parameter
*/
public function testCreateInstanceRawParameter()
{
$this->assertInstanceOf(
BookmarkRawFormatter::class,
$this->factory->getFormatter('raw')
);
}
/**
* Test creating an instance of BookmarkMarkdownFormatter from settings
*/
public function testCreateInstanceMarkdownSetting()
{
$this->conf->set('formatter', 'markdown');
$this->assertInstanceOf(BookmarkMarkdownFormatter::class, $this->factory->getFormatter());
}
/**
* Test creating an instance of BookmarkMarkdownFormatter from parameter
*/
public function testCreateInstanceMarkdownParameter()
{
$this->assertInstanceOf(
BookmarkMarkdownFormatter::class,
$this->factory->getFormatter('markdown')
);
}
}

View file

@ -1,101 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ShaarliContainer;
use Shaarli\Security\LoginManager;
use Shaarli\TestCase;
use Shaarli\Updater\Updater;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Uri;
class ShaarliAdminMiddlewareTest extends TestCase
{
protected const TMP_MOCK_FILE = '.tmp';
/** @var ShaarliContainer */
protected $container;
/** @var ShaarliMiddleware */
protected $middleware;
public function setUp(): void
{
$this->container = $this->createMock(ShaarliContainer::class);
touch(static::TMP_MOCK_FILE);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->updater = $this->createMock(Updater::class);
$this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path'];
$this->middleware = new ShaarliAdminMiddleware($this->container);
}
public function tearDown(): void
{
unlink(static::TMP_MOCK_FILE);
}
/**
* Try to access an admin controller while logged out -> redirected to login page.
*/
public function testMiddlewareWhileLoggedOut(): void
{
$this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(false);
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, function () {
});
static::assertSame(302, $result->getStatusCode());
static::assertSame(
'/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'),
$result->getHeader('location')[0]
);
}
/**
* Process controller while logged in.
*/
public function testMiddlewareWhileLoggedIn(): void
{
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (Request $request, Response $response): Response {
return $response->withStatus(418); // I'm a tea pot
};
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertSame(418, $result->getStatusCode());
}
}

View file

@ -1,222 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ShaarliContainer;
use Shaarli\Front\Exception\LoginBannedException;
use Shaarli\Front\Exception\UnauthorizedException;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\TestCase;
use Shaarli\Updater\Updater;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Uri;
class ShaarliMiddlewareTest extends TestCase
{
protected const TMP_MOCK_FILE = '.tmp';
/** @var ShaarliContainer */
protected $container;
/** @var ShaarliMiddleware */
protected $middleware;
public function setUp(): void
{
$this->container = $this->createMock(ShaarliContainer::class);
touch(static::TMP_MOCK_FILE);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path'];
$this->middleware = new ShaarliMiddleware($this->container);
}
public function tearDown(): void
{
unlink(static::TMP_MOCK_FILE);
}
/**
* Test middleware execution with valid controller call
*/
public function testMiddlewareExecution(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (Request $request, Response $response): Response {
return $response->withStatus(418); // I'm a tea pot
};
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertInstanceOf(Response::class, $result);
static::assertSame(418, $result->getStatusCode());
}
/**
* Test middleware execution with controller throwing a known front exception.
* The exception should be thrown to be later handled by the error handler.
*/
public function testMiddlewareExecutionWithFrontException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (): void {
$exception = new LoginBannedException();
throw new $exception();
};
$pageBuilder = $this->createMock(PageBuilder::class);
$pageBuilder->method('render')->willReturnCallback(function (string $message): string {
return $message;
});
$this->container->pageBuilder = $pageBuilder;
$this->expectException(LoginBannedException::class);
$this->middleware->__invoke($request, $response, $controller);
}
/**
* Test middleware execution with controller throwing a not authorized exception
* The middle should send a redirection response to the login page.
*/
public function testMiddlewareExecutionWithUnauthorizedException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (): void {
throw new UnauthorizedException();
};
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertSame(302, $result->getStatusCode());
static::assertSame(
'/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'),
$result->getHeader('location')[0]
);
}
/**
* Test middleware execution with controller throwing a not authorized exception.
* The exception should be thrown to be later handled by the error handler.
*/
public function testMiddlewareExecutionWithServerException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$dummyException = new class () extends \Exception {
};
$response = new Response();
$controller = function () use ($dummyException): void {
throw $dummyException;
};
$parameters = [];
$this->container->pageBuilder = $this->createMock(PageBuilder::class);
$this->container->pageBuilder->method('render')->willReturnCallback(function (string $message): string {
return $message;
});
$this->container->pageBuilder
->method('assign')
->willReturnCallback(function (string $key, string $value) use (&$parameters): void {
$parameters[$key] = $value;
})
;
$this->expectException(get_class($dummyException));
$this->middleware->__invoke($request, $response, $controller);
}
public function testMiddlewareExecutionWithUpdates(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (Request $request, Response $response): Response {
return $response->withStatus(418); // I'm a tea pot
};
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key): string {
return $key;
});
$this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
$this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
$this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
$this->container->updater = $this->createMock(Updater::class);
$this->container->updater
->expects(static::once())
->method('update')
->willReturn(['update123'])
;
$this->container->updater->method('getDoneUpdates')->willReturn($updates = ['update123', 'other']);
$this->container->updater
->expects(static::once())
->method('writeUpdates')
->with('resource.updates', $updates)
;
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertInstanceOf(Response::class, $result);
static::assertSame(418, $result->getStatusCode());
}
}

View file

@ -1,250 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class ConfigureControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ConfigureController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ConfigureController($this->container);
}
/**
* Test displaying configure page - it should display all config variables
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key) {
return $key;
});
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('configure', (string) $result->getBody());
static::assertSame('Configure - general.title', $assignedVariables['pagetitle']);
static::assertSame('general.title', $assignedVariables['title']);
static::assertSame('resource.theme', $assignedVariables['theme']);
static::assertEmpty($assignedVariables['theme_available']);
static::assertSame(['default', 'markdown', 'markdownExtra'], $assignedVariables['formatter_available']);
static::assertNotEmpty($assignedVariables['continents']);
static::assertNotEmpty($assignedVariables['cities']);
static::assertSame('general.retrieve_description', $assignedVariables['retrieve_description']);
static::assertSame('privacy.default_private_links', $assignedVariables['private_links_default']);
static::assertSame('security.session_protection_disabled', $assignedVariables['session_protection_disabled']);
static::assertSame('feed.rss_permalinks', $assignedVariables['enable_rss_permalinks']);
static::assertSame('updates.check_updates', $assignedVariables['enable_update_check']);
static::assertSame('privacy.hide_public_links', $assignedVariables['hide_public_links']);
static::assertSame('api.enabled', $assignedVariables['api_enabled']);
static::assertSame('api.secret', $assignedVariables['api_secret']);
static::assertCount(7, $assignedVariables['languages']);
static::assertArrayHasKey('gd_enabled', $assignedVariables);
static::assertSame('thumbnails.mode', $assignedVariables['thumbnails_mode']);
}
/**
* Test posting a new config - make sure that everything is saved properly, without errors.
*/
public function testSaveNewConfig(): void
{
$session = [];
$this->assignSessionVars($session);
$parameters = [
'token' => 'token',
'continent' => 'Europe',
'city' => 'Moscow',
'title' => 'Shaarli',
'titleLink' => './',
'retrieveDescription' => 'on',
'theme' => 'vintage',
'disablesessionprotection' => null,
'privateLinkByDefault' => true,
'enableRssPermalinks' => true,
'updateCheck' => false,
'hidePublicLinks' => 'on',
'enableApi' => 'on',
'apiSecret' => 'abcdef',
'formatter' => 'markdown',
'language' => 'fr',
'enableThumbnails' => Thumbnailer::MODE_NONE,
];
$parametersConfigMapping = [
'general.timezone' => $parameters['continent'] . '/' . $parameters['city'],
'general.title' => $parameters['title'],
'general.header_link' => $parameters['titleLink'],
'general.retrieve_description' => !!$parameters['retrieveDescription'],
'resource.theme' => $parameters['theme'],
'security.session_protection_disabled' => !!$parameters['disablesessionprotection'],
'privacy.default_private_links' => !!$parameters['privateLinkByDefault'],
'feed.rss_permalinks' => !!$parameters['enableRssPermalinks'],
'updates.check_updates' => !!$parameters['updateCheck'],
'privacy.hide_public_links' => !!$parameters['hidePublicLinks'],
'api.enabled' => !!$parameters['enableApi'],
'api.secret' => $parameters['apiSecret'],
'formatter' => $parameters['formatter'],
'translation.language' => $parameters['language'],
'thumbnails.mode' => $parameters['enableThumbnails'],
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
if (false === array_key_exists($key, $parameters)) {
static::fail('unknown key: ' . $key);
}
return $parameters[$key];
});
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->expects(static::atLeastOnce())
->method('set')
->willReturnCallback(function (string $key, $value) use ($parametersConfigMapping): void {
if (false === array_key_exists($key, $parametersConfigMapping)) {
static::fail('unknown key: ' . $key);
}
static::assertSame($parametersConfigMapping[$key], $value);
});
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
}
/**
* Test posting a new config - wrong token.
*/
public function testSaveNewConfigWrongToken(): void
{
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(false);
$this->container->conf->expects(static::never())->method('set');
$this->container->conf->expects(static::never())->method('write');
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(WrongTokenException::class);
$this->controller->save($request, $response);
}
/**
* Test posting a new config - thumbnail activation.
*/
public function testSaveNewConfigThumbnailsActivation(): void
{
$session = [];
$this->assignSessionVars($session);
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')->willReturnCallback(function (string $key) {
if ('enableThumbnails' === $key) {
return Thumbnailer::MODE_ALL;
}
return $key;
})
;
$response = new Response();
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertStringContainsString(
'You have enabled or changed thumbnails mode',
$session[SessionManager::KEY_WARNING_MESSAGES][0]
);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
}
/**
* Test posting a new config - thumbnail activation.
*/
public function testSaveNewConfigThumbnailsAlreadyActive(): void
{
$session = [];
$this->assignSessionVars($session);
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')->willReturnCallback(function (string $key) {
if ('enableThumbnails' === $key) {
return Thumbnailer::MODE_ALL;
}
return $key;
})
;
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->expects(static::atLeastOnce())
->method('get')
->willReturnCallback(function (string $key): string {
if ('thumbnails.mode' === $key) {
return Thumbnailer::MODE_ALL;
}
return $key;
})
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
}
}

View file

@ -1,166 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\BookmarkRawFormatter;
use Shaarli\Netscape\NetscapeBookmarkUtils;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ExportControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ExportController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ExportController($this->container);
}
/**
* Test displaying export page
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('export', (string) $result->getBody());
static::assertSame('Export - Shaarli', $assignedVariables['pagetitle']);
}
/**
* Test posting an export request
*/
public function testExportDefault(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$parameters = [
'selection' => 'all',
'prepend_note_url' => 'on',
];
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
return $parameters[$key] ?? null;
});
$response = new Response();
$bookmarks = [
(new Bookmark())->setUrl('http://link1.tld')->setTitle('Title 1'),
(new Bookmark())->setUrl('http://link2.tld')->setTitle('Title 2'),
];
$this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
$this->container->netscapeBookmarkUtils
->expects(static::once())
->method('filterAndFormat')
->willReturnCallback(
function (
BookmarkFormatter $formatter,
string $selection,
bool $prependNoteUrl,
string $indexUrl
) use (
$parameters,
$bookmarks
): array {
static::assertInstanceOf(BookmarkRawFormatter::class, $formatter);
static::assertSame($parameters['selection'], $selection);
static::assertTrue($prependNoteUrl);
static::assertSame('http://shaarli/subfolder/', $indexUrl);
return $bookmarks;
}
)
;
$result = $this->controller->export($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('export.bookmarks', (string) $result->getBody());
static::assertSame(['text/html; charset=utf-8'], $result->getHeader('content-type'));
static::assertRegExp(
'/attachment; filename=bookmarks_all_[\d]{8}_[\d]{6}\.html/',
$result->getHeader('content-disposition')[0]
);
static::assertNotEmpty($assignedVariables['date']);
static::assertSame(PHP_EOL, $assignedVariables['eol']);
static::assertSame('all', $assignedVariables['selection']);
static::assertSame($bookmarks, $assignedVariables['links']);
}
/**
* Test posting an export request - without selection parameter
*/
public function testExportSelectionMissing(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Please select an export mode.'])
;
$result = $this->controller->export($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/export'], $result->getHeader('location'));
}
/**
* Test posting an export request - without selection parameter
*/
public function testExportErrorEncountered(): void
{
$parameters = [
'selection' => 'all',
];
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
return $parameters[$key] ?? null;
});
$response = new Response();
$this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
$this->container->netscapeBookmarkUtils
->expects(static::once())
->method('filterAndFormat')
->willThrowException(new \Exception($message = 'error message'));
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, [$message])
;
$result = $this->controller->export($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/export'], $result->getHeader('location'));
}
}

View file

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Container\ShaarliTestContainer;
use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
use Shaarli\History;
/**
* Trait FrontControllerMockHelper
*
* Helper trait used to initialize the ShaarliContainer and mock its services for admin controller tests.
*
* @property ShaarliTestContainer $container
*/
trait FrontAdminControllerMockHelper
{
use FrontControllerMockHelper {
FrontControllerMockHelper::createContainer as parentCreateContainer;
}
/**
* Mock the container instance
*/
protected function createContainer(): void
{
$this->parentCreateContainer();
$this->container->history = $this->createMock(History::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->sessionManager->method('checkToken')->willReturn(true);
}
/**
* Pass a reference of an array which will be populated by `sessionManager->setSessionParameter`
* calls during execution.
*
* @param mixed $variables Array reference to populate.
*/
protected function assignSessionVars(array &$variables): void
{
$this->container->sessionManager
->expects(static::atLeastOnce())
->method('setSessionParameter')
->willReturnCallback(function ($key, $value) use (&$variables) {
$variables[$key] = $value;
return $this->container->sessionManager;
})
;
}
}

View file

@ -1,151 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Psr\Http\Message\UploadedFileInterface;
use Shaarli\Netscape\NetscapeBookmarkUtils;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\UploadedFile;
class ImportControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ImportController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ImportController($this->container);
}
/**
* Test displaying import page
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('import', (string) $result->getBody());
static::assertSame('Import - Shaarli', $assignedVariables['pagetitle']);
static::assertIsInt($assignedVariables['maxfilesize']);
static::assertRegExp('/\d+[KM]iB/', $assignedVariables['maxfilesizeHuman']);
}
/**
* Test importing a file with default and valid parameters
*/
public function testImportDefault(): void
{
$parameters = [
'abc' => 'def',
'other' => 'param',
];
$requestFile = new UploadedFile('file', 'name', 'type', 123);
$request = $this->createMock(Request::class);
$request->method('getParams')->willReturnCallback(function () use ($parameters) {
return $parameters;
});
$request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
$response = new Response();
$this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
$this->container->netscapeBookmarkUtils
->expects(static::once())
->method('import')
->willReturnCallback(
function (
array $post,
UploadedFileInterface $file
) use (
$parameters,
$requestFile
): string {
static::assertSame($parameters, $post);
static::assertSame($requestFile, $file);
return 'status';
}
)
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES, ['status'])
;
$result = $this->controller->import($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
}
/**
* Test posting an import request - without import file
*/
public function testImportFileMissing(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['No import file provided.'])
;
$result = $this->controller->import($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
}
/**
* Test posting an import request - with an empty file
*/
public function testImportEmptyFile(): void
{
$requestFile = new UploadedFile('file', 'name', 'type', 0);
$request = $this->createMock(Request::class);
$request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
$response = new Response();
$this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
$this->container->netscapeBookmarkUtils->expects(static::never())->method('filterAndFormat');
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->willReturnCallback(function (string $key, array $value): SessionManager {
static::assertSame(SessionManager::KEY_ERROR_MESSAGES, $key);
static::assertStringStartsWith('The file you are trying to upload is probably bigger', $value[0]);
return $this->container->sessionManager;
})
;
$result = $this->controller->import($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
}
}

View file

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Security\CookieManager;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class LogoutControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var LogoutController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new LogoutController($this->container);
}
public function testValidControllerInvoke(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->expects(static::once())->method('logout');
$this->container->cookieManager = $this->createMock(CookieManager::class);
$this->container->cookieManager
->expects(static::once())
->method('setCookieParameter')
->with(CookieManager::STAY_SIGNED_IN, 'false', 0, '/subfolder/')
;
$result = $this->controller->index($request, $response);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
}

View file

@ -1,409 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFilter;
use Shaarli\Bookmark\SearchResult;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ManageTagControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ManageTagController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ManageTagController($this->container);
}
/**
* Test displaying manage tag page
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->with('fromtag')->willReturn('fromtag');
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('changetag', (string) $result->getBody());
static::assertSame('fromtag', $assignedVariables['fromtag']);
static::assertSame('@', $assignedVariables['tags_separator']);
static::assertSame('Manage tags - Shaarli', $assignedVariables['pagetitle']);
}
/**
* Test displaying manage tag page
*/
public function testIndexWhitespaceSeparator(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key) {
return $key === 'general.tags_separator' ? ' ' : $key;
});
$request = $this->createMock(Request::class);
$response = new Response();
$this->controller->index($request, $response);
static::assertSame('&nbsp;', $assignedVariables['tags_separator']);
static::assertSame('whitespace', $assignedVariables['tags_separator_desc']);
}
/**
* Test posting a tag update - rename tag - valid info provided.
*/
public function testSaveRenameTagValid(): void
{
$session = [];
$this->assignSessionVars($session);
$requestParameters = [
'renametag' => 'rename',
'fromtag' => 'old-tag',
'totag' => 'new-tag',
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($requestParameters): ?string {
return $requestParameters[$key] ?? null;
})
;
$response = new Response();
$bookmark1 = $this->createMock(Bookmark::class);
$bookmark2 = $this->createMock(Bookmark::class);
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true)
->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult {
$bookmark1->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag');
$bookmark2->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag');
return SearchResult::getSearchResult([$bookmark1, $bookmark2]);
})
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('set')
->withConsecutive([$bookmark1, false], [$bookmark2, false])
;
$this->container->bookmarkService->expects(static::once())->method('save');
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/?searchtags=new-tag'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['The tag was renamed in 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
}
/**
* Test posting a tag update - delete tag - valid info provided.
*/
public function testSaveDeleteTagValid(): void
{
$session = [];
$this->assignSessionVars($session);
$requestParameters = [
'deletetag' => 'delete',
'fromtag' => 'old-tag',
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($requestParameters): ?string {
return $requestParameters[$key] ?? null;
})
;
$response = new Response();
$bookmark1 = $this->createMock(Bookmark::class);
$bookmark2 = $this->createMock(Bookmark::class);
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true)
->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult {
$bookmark1->expects(static::once())->method('deleteTag')->with('old-tag');
$bookmark2->expects(static::once())->method('deleteTag')->with('old-tag');
return SearchResult::getSearchResult([$bookmark1, $bookmark2]);
})
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('set')
->withConsecutive([$bookmark1, false], [$bookmark2, false])
;
$this->container->bookmarkService->expects(static::once())->method('save');
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['The tag was removed from 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
}
/**
* Test posting a tag update - wrong token.
*/
public function testSaveWrongToken(): void
{
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(false);
$this->container->conf->expects(static::never())->method('set');
$this->container->conf->expects(static::never())->method('write');
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(WrongTokenException::class);
$this->controller->save($request, $response);
}
/**
* Test posting a tag update - rename tag - missing "FROM" tag.
*/
public function testSaveRenameTagMissingFrom(): void
{
$session = [];
$this->assignSessionVars($session);
$requestParameters = [
'renametag' => 'rename',
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($requestParameters): ?string {
return $requestParameters[$key] ?? null;
})
;
$response = new Response();
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
}
/**
* Test posting a tag update - delete tag - missing "FROM" tag.
*/
public function testSaveDeleteTagMissingFrom(): void
{
$session = [];
$this->assignSessionVars($session);
$requestParameters = [
'deletetag' => 'delete',
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($requestParameters): ?string {
return $requestParameters[$key] ?? null;
})
;
$response = new Response();
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
}
/**
* Test posting a tag update - rename tag - missing "TO" tag.
*/
public function testSaveRenameTagMissingTo(): void
{
$session = [];
$this->assignSessionVars($session);
$requestParameters = [
'renametag' => 'rename',
'fromtag' => 'old-tag'
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($requestParameters): ?string {
return $requestParameters[$key] ?? null;
})
;
$response = new Response();
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
}
/**
* Test changeSeparator to '#': redirection + success message.
*/
public function testChangeSeparatorValid(): void
{
$toSeparator = '#';
$session = [];
$this->assignSessionVars($session);
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($toSeparator): ?string {
return $key === 'separator' ? $toSeparator : $key;
})
;
$response = new Response();
$this->container->conf
->expects(static::once())
->method('set')
->with('general.tags_separator', $toSeparator, true, true)
;
$result = $this->controller->changeSeparator($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertSame(
['Your tags separator setting has been updated!'],
$session[SessionManager::KEY_SUCCESS_MESSAGES]
);
}
/**
* Test changeSeparator to '#@' (too long): redirection + error message.
*/
public function testChangeSeparatorInvalidTooLong(): void
{
$toSeparator = '#@';
$session = [];
$this->assignSessionVars($session);
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($toSeparator): ?string {
return $key === 'separator' ? $toSeparator : $key;
})
;
$response = new Response();
$this->container->conf->expects(static::never())->method('set');
$result = $this->controller->changeSeparator($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertSame(
['Tags separator must be a single character.'],
$session[SessionManager::KEY_ERROR_MESSAGES]
);
}
/**
* Test changeSeparator to '#@' (too long): redirection + error message.
*/
public function testChangeSeparatorInvalidReservedCharacter(): void
{
$toSeparator = '*';
$session = [];
$this->assignSessionVars($session);
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParam')
->willReturnCallback(function (string $key) use ($toSeparator): ?string {
return $key === 'separator' ? $toSeparator : $key;
})
;
$response = new Response();
$this->container->conf->expects(static::never())->method('set');
$result = $this->controller->changeSeparator($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertStringStartsWith(
'These characters are reserved and can\'t be used as tags separator',
$session[SessionManager::KEY_ERROR_MESSAGES][0]
);
}
}

View file

@ -1,203 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\OpenShaarliPasswordException;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class PasswordControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var PasswordController */
protected $controller;
/** @var mixed[] Variables assigned to the template */
protected $assignedVariables = [];
public function setUp(): void
{
$this->createContainer();
$this->assignTemplateVars($this->assignedVariables);
$this->controller = new PasswordController($this->container);
}
/**
* Test displaying the change password page.
*/
public function testGetPage(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('changepassword', (string) $result->getBody());
static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
}
/**
* Change the password with valid parameters
*/
public function testPostNewPasswordDefault(): void
{
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key): string {
if ('oldpassword' === $key) {
return 'old';
}
if ('setpassword' === $key) {
return 'new';
}
return $key;
});
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ('credentials.hash' === $key) {
return sha1('old' . 'credentials.login' . 'credentials.salt');
}
return strpos($key, 'credentials') !== false ? $key : $default;
});
$this->container->conf->expects(static::once())->method('write')->with(true);
$this->container->conf
->method('set')
->willReturnCallback(function (string $key, string $value) {
if ('credentials.hash' === $key) {
static::assertSame(sha1('new' . 'credentials.login' . 'credentials.salt'), $value);
}
})
;
$result = $this->controller->change($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('changepassword', (string) $result->getBody());
static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
}
/**
* Change the password with a wrong existing password
*/
public function testPostNewPasswordWrongOldPassword(): void
{
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key): string {
if ('oldpassword' === $key) {
return 'wrong';
}
if ('setpassword' === $key) {
return 'new';
}
return $key;
});
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ('credentials.hash' === $key) {
return sha1('old' . 'credentials.login' . 'credentials.salt');
}
return strpos($key, 'credentials') !== false ? $key : $default;
});
$this->container->conf->expects(static::never())->method('set');
$this->container->conf->expects(static::never())->method('write');
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['The old password is not correct.'])
;
$result = $this->controller->change($request, $response);
static::assertSame(400, $result->getStatusCode());
static::assertSame('changepassword', (string) $result->getBody());
static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
}
/**
* Change the password with a wrong existing password
*/
public function testPostNewPasswordWrongToken(): void
{
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(false);
$this->container->conf->expects(static::never())->method('set');
$this->container->conf->expects(static::never())->method('write');
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(WrongTokenException::class);
$this->controller->change($request, $response);
}
/**
* Change the password with an empty new password
*/
public function testPostNewEmptyPassword(): void
{
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['You must provide the current and new password to change it.'])
;
$this->container->conf->expects(static::never())->method('set');
$this->container->conf->expects(static::never())->method('write');
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key): string {
if ('oldpassword' === $key) {
return 'old';
}
if ('setpassword' === $key) {
return '';
}
return $key;
});
$response = new Response();
$result = $this->controller->change($request, $response);
static::assertSame(400, $result->getStatusCode());
static::assertSame('changepassword', (string) $result->getBody());
static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
}
/**
* Change the password on an open shaarli
*/
public function testPostNewPasswordOnOpenShaarli(): void
{
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->with('security.open_shaarli')->willReturn(true);
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(OpenShaarliPasswordException::class);
$this->controller->change($request, $response);
}
}

View file

@ -1,209 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Plugin\PluginManager;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class PluginsControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
protected const PLUGIN_NAMES = ['plugin1', 'plugin2', 'plugin3', 'plugin4'];
/** @var PluginsController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new PluginsController($this->container);
mkdir($path = __DIR__ . '/folder');
PluginManager::$PLUGINS_PATH = $path;
array_map(function (string $plugin) use ($path) {
touch($path . '/' . $plugin);
}, static::PLUGIN_NAMES);
}
public function tearDown(): void
{
$path = __DIR__ . '/folder';
array_map(function (string $plugin) use ($path) {
unlink($path . '/' . $plugin);
}, static::PLUGIN_NAMES);
rmdir($path);
}
/**
* Test displaying plugins admin page
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$data = [
'plugin1' => ['order' => 2, 'other' => 'field'],
'plugin2' => ['order' => 1],
'plugin3' => ['order' => false, 'abc' => 'def'],
'plugin4' => [],
];
$this->container->pluginManager
->expects(static::once())
->method('getPluginsMeta')
->willReturn($data);
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('pluginsadmin', (string) $result->getBody());
static::assertSame('Plugin Administration - Shaarli', $assignedVariables['pagetitle']);
static::assertSame(
['plugin2' => $data['plugin2'], 'plugin1' => $data['plugin1']],
$assignedVariables['enabledPlugins']
);
static::assertSame(
['plugin3' => $data['plugin3'], 'plugin4' => $data['plugin4']],
$assignedVariables['disabledPlugins']
);
}
/**
* Test save plugins admin page
*/
public function testSaveEnabledPlugins(): void
{
$parameters = [
'plugin1' => 'on',
'order_plugin1' => '2',
'plugin2' => 'on',
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParams')
->willReturnCallback(function () use ($parameters): array {
return $parameters;
})
;
$response = new Response();
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_plugin_parameters', $parameters)
;
$this->container->conf
->expects(static::atLeastOnce())
->method('set')
->with('general.enabled_plugins', ['plugin1', 'plugin2'])
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
}
/**
* Test save plugin parameters
*/
public function testSavePluginParameters(): void
{
$parameters = [
'parameters_form' => true,
'parameter1' => 'blip',
'parameter2' => 'blop',
'token' => 'this parameter should not be saved'
];
$request = $this->createMock(Request::class);
$request
->expects(static::atLeastOnce())
->method('getParams')
->willReturnCallback(function () use ($parameters): array {
return $parameters;
})
;
$response = new Response();
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_plugin_parameters', $parameters)
;
$this->container->conf
->expects(static::exactly(2))
->method('set')
->withConsecutive(['plugins.parameter1', 'blip'], ['plugins.parameter2', 'blop'])
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
}
/**
* Test save plugin parameters - error encountered
*/
public function testSaveWithError(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->expects(static::atLeastOnce())
->method('write')
->willThrowException(new \Exception($message = 'error message'))
;
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(true);
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(
SessionManager::KEY_ERROR_MESSAGES,
['Error while saving plugin configuration: ' . PHP_EOL . $message]
)
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
}
/**
* Test save plugin parameters - wrong token
*/
public function testSaveWrongToken(): void
{
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(false);
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(WrongTokenException::class);
$this->controller->save($request, $response);
}
}

View file

@ -1,184 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Config\ConfigManager;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Test Server administration controller.
*/
class ServerControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ServerController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ServerController($this->container);
// initialize dummy cache
@mkdir('sandbox/');
foreach (['pagecache', 'tmp', 'cache'] as $folder) {
@mkdir('sandbox/' . $folder);
@touch('sandbox/' . $folder . '/.htaccess');
@touch('sandbox/' . $folder . '/1');
@touch('sandbox/' . $folder . '/2');
}
}
public function tearDown(): void
{
foreach (['pagecache', 'tmp', 'cache'] as $folder) {
@unlink('sandbox/' . $folder . '/.htaccess');
@unlink('sandbox/' . $folder . '/1');
@unlink('sandbox/' . $folder . '/2');
@rmdir('sandbox/' . $folder);
}
}
/**
* Test default display of server administration page.
*/
public function testIndex(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('server', (string) $result->getBody());
static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
static::assertArrayHasKey('php_eol', $assignedVariables);
static::assertArrayHasKey('php_extensions', $assignedVariables);
static::assertArrayHasKey('permissions', $assignedVariables);
static::assertEmpty($assignedVariables['permissions']);
static::assertRegExp(
'#https://github\.com/shaarli/Shaarli/releases/tag/v\d+\.\d+\.\d+#',
$assignedVariables['release_url']
);
static::assertRegExp('#v\d+\.\d+\.\d+#', $assignedVariables['latest_version']);
static::assertRegExp('#(v\d+\.\d+\.\d+|dev)#', $assignedVariables['current_version']);
static::assertArrayHasKey('index_url', $assignedVariables);
static::assertArrayHasKey('client_ip', $assignedVariables);
static::assertArrayHasKey('trusted_proxies', $assignedVariables);
static::assertSame('Server administration - Shaarli', $assignedVariables['pagetitle']);
}
/**
* Test clearing the main cache
*/
public function testClearMainCache(): void
{
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ($key === 'resource.page_cache') {
return 'sandbox/pagecache';
} elseif ($key === 'resource.raintpl_tmp') {
return 'sandbox/tmp';
} elseif ($key === 'resource.thumbnails_cache') {
return 'sandbox/cache';
} else {
return $default;
}
});
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES, ['Shaarli\'s cache folder has been cleared!'])
;
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('type')->willReturn('main');
$response = new Response();
$result = $this->controller->clearCache($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
static::assertFileNotExists('sandbox/pagecache/1');
static::assertFileNotExists('sandbox/pagecache/2');
static::assertFileNotExists('sandbox/tmp/1');
static::assertFileNotExists('sandbox/tmp/2');
static::assertFileExists('sandbox/pagecache/.htaccess');
static::assertFileExists('sandbox/tmp/.htaccess');
static::assertFileExists('sandbox/cache');
static::assertFileExists('sandbox/cache/.htaccess');
static::assertFileExists('sandbox/cache/1');
static::assertFileExists('sandbox/cache/2');
}
/**
* Test clearing thumbnails cache
*/
public function testClearThumbnailsCache(): void
{
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ($key === 'resource.page_cache') {
return 'sandbox/pagecache';
} elseif ($key === 'resource.raintpl_tmp') {
return 'sandbox/tmp';
} elseif ($key === 'resource.thumbnails_cache') {
return 'sandbox/cache';
} else {
return $default;
}
});
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->willReturnCallback(function (string $key, array $value): SessionManager {
static::assertSame(SessionManager::KEY_WARNING_MESSAGES, $key);
static::assertCount(1, $value);
static::assertStringStartsWith('Thumbnails cache has been cleared.', $value[0]);
return $this->container->sessionManager;
});
;
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('type')->willReturn('thumbnails');
$response = new Response();
$result = $this->controller->clearCache($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
static::assertFileNotExists('sandbox/cache/1');
static::assertFileNotExists('sandbox/cache/2');
static::assertFileExists('sandbox/cache/.htaccess');
static::assertFileExists('sandbox/pagecache');
static::assertFileExists('sandbox/pagecache/.htaccess');
static::assertFileExists('sandbox/pagecache/1');
static::assertFileExists('sandbox/pagecache/2');
static::assertFileExists('sandbox/tmp');
static::assertFileExists('sandbox/tmp/.htaccess');
static::assertFileExists('sandbox/tmp/1');
static::assertFileExists('sandbox/tmp/2');
}
}

View file

@ -1,177 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class SessionFilterControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var SessionFilterController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new SessionFilterController($this->container);
}
/**
* Visibility - Default call for private filter while logged in without current value
*/
public function testVisibility(): void
{
$arg = ['visibility' => 'private'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_VISIBILITY, 'private')
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->visibility($request, $response, $arg);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
}
/**
* Visibility - Toggle off private visibility
*/
public function testVisibilityToggleOff(): void
{
$arg = ['visibility' => 'private'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->sessionManager
->method('getSessionParameter')
->with(SessionManager::KEY_VISIBILITY)
->willReturn('private')
;
$this->container->sessionManager
->expects(static::never())
->method('setSessionParameter')
;
$this->container->sessionManager
->expects(static::once())
->method('deleteSessionParameter')
->with(SessionManager::KEY_VISIBILITY)
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->visibility($request, $response, $arg);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
}
/**
* Visibility - Change private to public
*/
public function testVisibilitySwitch(): void
{
$arg = ['visibility' => 'private'];
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->sessionManager
->method('getSessionParameter')
->with(SessionManager::KEY_VISIBILITY)
->willReturn('public')
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_VISIBILITY, 'private')
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->visibility($request, $response, $arg);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Visibility - With invalid value - should remove any visibility setting
*/
public function testVisibilityInvalidValue(): void
{
$arg = ['visibility' => 'test'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->sessionManager
->expects(static::never())
->method('setSessionParameter')
;
$this->container->sessionManager
->expects(static::once())
->method('deleteSessionParameter')
->with(SessionManager::KEY_VISIBILITY)
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->visibility($request, $response, $arg);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
}
/**
* Visibility - Try to change visibility while logged out
*/
public function testVisibilityLoggedOut(): void
{
$arg = ['visibility' => 'test'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(false);
$this->container->sessionManager
->expects(static::never())
->method('setSessionParameter')
;
$this->container->sessionManager
->expects(static::never())
->method('deleteSessionParameter')
->with(SessionManager::KEY_VISIBILITY)
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->visibility($request, $response, $arg);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
}
}

View file

@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Config\ConfigManager;
use Shaarli\Formatter\BookmarkMarkdownFormatter;
use Shaarli\Http\HttpAccess;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ShaareAddControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareAddController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareAddController($this->container);
}
/**
* Test displaying add link page
*/
public function testAddShaare(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$expectedTags = [
'tag1' => 32,
'tag2' => 24,
'tag3' => 1,
];
$this->container->bookmarkService
->expects(static::once())
->method('bookmarksCountPerTag')
->willReturn($expectedTags)
;
$expectedTags = array_merge($expectedTags, [BookmarkMarkdownFormatter::NO_MD_TAG => 1]);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
return $key === 'formatter' ? 'markdown' : $default;
});
$result = $this->controller->addShaare($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('addlink', (string) $result->getBody());
static::assertSame('Shaare a new link - Shaarli', $assignedVariables['pagetitle']);
static::assertFalse($assignedVariables['default_private_links']);
static::assertTrue($assignedVariables['async_metadata']);
static::assertSame($expectedTags, $assignedVariables['tags']);
}
/**
* Test displaying add link page
*/
public function testAddShaareWithoutMd(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$expectedTags = [
'tag1' => 32,
'tag2' => 24,
'tag3' => 1,
];
$this->container->bookmarkService
->expects(static::once())
->method('bookmarksCountPerTag')
->willReturn($expectedTags)
;
$result = $this->controller->addShaare($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('addlink', (string) $result->getBody());
static::assertSame($expectedTags, $assignedVariables['tags']);
}
}

View file

@ -1,380 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\BookmarkRawFormatter;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaareManageController;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class AddOrDeleteTagTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareManageController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareManageController($this->container);
}
/**
* Add 1 tag to 1 bookmark
*/
public function testAddOneTagOnOneBookmark(): void
{
$parameters = ['id' => '123', 'tag' => 'newtag', 'action' => 'add'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
->setTagsString('first second');
static::assertSame(['first', 'second'], $bookmark->getTags());
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(['first', 'second', 'newtag'], $bookmark->getTags());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Add 2 tags to 2 bookmarks
*/
public function testAddTwoTagsOnTwoBookmarks(): void
{
$parameters = ['id' => '123 456', 'tag' => 'newtag@othertag', 'action' => 'add'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark1 = (new Bookmark())
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
->setTagsString('first second');
$bookmark2 = (new Bookmark())
->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123');
static::assertSame(['first', 'second'], $bookmark1->getTags());
static::assertSame([], $bookmark2->getTags());
$this->container->bookmarkService->expects(static::exactly(2))->method('get')
->withConsecutive([123], [456])
->willReturnOnConsecutiveCalls($bookmark1, $bookmark2);
$this->container->bookmarkService->expects(static::exactly(2))->method('set')
->withConsecutive([$bookmark1, false], [$bookmark2, false]);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function (): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::exactly(2))
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(['first', 'second', 'newtag', 'othertag'], $bookmark1->getTags());
static::assertSame(['newtag', 'othertag'], $bookmark2->getTags());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete 1 tag to 1 bookmark
*/
public function testDeleteOneTagOnOneBookmark(): void
{
$parameters = ['id' => '123', 'tag' => 'second', 'action' => 'delete'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
->setTagsString('first second third');
static::assertSame(['first', 'second', 'third'], $bookmark->getTags());
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(['first', 'third'], $bookmark->getTags());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete 2 tags to 2 bookmarks
*/
public function testDeleteTwoTagOnTwoBookmarks(): void
{
$parameters = ['id' => '123 456', 'tag' => 'second@first', 'action' => 'delete'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark1 = (new Bookmark())
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
->setTagsString('first second third other');
$bookmark2 = (new Bookmark())
->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123')
->setTagsString('first second');
static::assertSame(['first', 'second', 'third', 'other'], $bookmark1->getTags());
static::assertSame(['first', 'second'], $bookmark2->getTags());
$this->container->bookmarkService->expects(static::exactly(2))->method('get')
->withConsecutive([123], [456])
->willReturnOnConsecutiveCalls($bookmark1, $bookmark2);
$this->container->bookmarkService->expects(static::exactly(2))->method('set')
->withConsecutive([$bookmark1, false], [$bookmark2, false]);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function (): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::exactly(2))
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(['third', 'other'], $bookmark1->getTags());
static::assertSame([], $bookmark2->getTags());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test add a tag without passing an ID.
*/
public function testAddTagWithoutId(): void
{
$parameters = ['tag' => 'newtag', 'action' => 'add'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test add a tag without passing an ID.
*/
public function testDeleteTagWithoutId(): void
{
$parameters = ['tag' => 'newtag', 'action' => 'delete'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test add a tag without passing an action.
*/
public function testAddTagWithoutAction(): void
{
$parameters = ['id' => '123', 'tag' => 'newtag'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid action provided.'])
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test add a tag without passing a tag string value.
*/
public function testAddTagWithoutValue(): void
{
$parameters = ['id' => '123', 'tag' => '', 'action' => 'add'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.'])
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test delete a tag without passing a tag string value.
*/
public function testDeleteTagWithoutValue(): void
{
$parameters = ['id' => '123', 'tag' => '', 'action' => 'delete'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.'])
;
$result = $this->controller->addOrDeleteTags($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
}

View file

@ -1,418 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\BookmarkRawFormatter;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaareManageController;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ChangeVisibilityBookmarkTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareManageController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareManageController($this->container);
}
/**
* Change bookmark visibility - Set private - Single public bookmark with valid parameters
*/
public function testSetSingleBookmarkPrivate(): void
{
$parameters = ['id' => '123', 'newVisibility' => 'private'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false);
static::assertFalse($bookmark->isPrivate());
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->changeVisibility($request, $response);
static::assertTrue($bookmark->isPrivate());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Set public - Single private bookmark with valid parameters
*/
public function testSetSingleBookmarkPublic(): void
{
$parameters = ['id' => '123', 'newVisibility' => 'public'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true);
static::assertTrue($bookmark->isPrivate());
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturn(new BookmarkRawFormatter($this->container->conf, true))
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->changeVisibility($request, $response);
static::assertFalse($bookmark->isPrivate());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Set private on single already private bookmark
*/
public function testSetSinglePrivateBookmarkPrivate(): void
{
$parameters = ['id' => '123', 'newVisibility' => 'private'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true);
static::assertTrue($bookmark->isPrivate());
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturn(new BookmarkRawFormatter($this->container->conf, true))
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->changeVisibility($request, $response);
static::assertTrue($bookmark->isPrivate());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Set multiple bookmarks private
*/
public function testSetMultipleBookmarksPrivate(): void
{
$parameters = ['id' => '123 456 789', 'newVisibility' => 'private'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmarks = [
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false),
(new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456')->setPrivate(true),
(new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false),
];
$this->container->bookmarkService
->expects(static::exactly(3))
->method('get')
->withConsecutive([123], [456], [789])
->willReturnOnConsecutiveCalls(...$bookmarks)
;
$this->container->bookmarkService
->expects(static::exactly(3))
->method('set')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark, false];
}, $bookmarks))
;
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturn(new BookmarkRawFormatter($this->container->conf, true))
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::exactly(3))
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->changeVisibility($request, $response);
static::assertTrue($bookmarks[0]->isPrivate());
static::assertTrue($bookmarks[1]->isPrivate());
static::assertTrue($bookmarks[2]->isPrivate());
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Single bookmark not found.
*/
public function testChangeVisibilitySingleBookmarkNotFound(): void
{
$parameters = ['id' => '123', 'newVisibility' => 'private'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->willThrowException(new BookmarkNotFoundException())
;
$this->container->bookmarkService->expects(static::never())->method('set');
$this->container->bookmarkService->expects(static::never())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturn(new BookmarkRawFormatter($this->container->conf, true))
;
// Make sure that PluginManager hook is not triggered
$this->container->pluginManager
->expects(static::never())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->changeVisibility($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Multiple bookmarks with one not found.
*/
public function testChangeVisibilityMultipleBookmarksOneNotFound(): void
{
$parameters = ['id' => '123 456 789', 'newVisibility' => 'public'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmarks = [
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true),
(new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false),
];
$this->container->bookmarkService
->expects(static::exactly(3))
->method('get')
->withConsecutive([123], [456], [789])
->willReturnCallback(function (int $id) use ($bookmarks): Bookmark {
if ($id === 123) {
return $bookmarks[0];
}
if ($id === 789) {
return $bookmarks[1];
}
throw new BookmarkNotFoundException();
})
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('set')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark, false];
}, $bookmarks))
;
$this->container->bookmarkService->expects(static::once())->method('save');
// Make sure that PluginManager hook is not triggered
$this->container->pluginManager
->expects(static::exactly(2))
->method('executeHooks')
->with('save_link')
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.'])
;
$result = $this->controller->changeVisibility($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Invalid ID
*/
public function testChangeVisibilityInvalidId(): void
{
$parameters = ['id' => 'nope not an ID', 'newVisibility' => 'private'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->changeVisibility($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - Empty ID
*/
public function testChangeVisibilityEmptyId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->changeVisibility($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Change bookmark visibility - with invalid visibility
*/
public function testChangeVisibilityWithInvalidVisibility(): void
{
$parameters = ['id' => '123', 'newVisibility' => 'invalid'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid visibility provided.'])
;
$result = $this->controller->changeVisibility($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
}

View file

@ -1,427 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaareManageController;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class DeleteBookmarkTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareManageController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareManageController($this->container);
}
/**
* Delete bookmark - Single bookmark with valid parameters
*/
public function testDeleteSingleBookmark(): void
{
$parameters = ['id' => '123'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/shaare/abcdef';
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123');
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('remove')->with($bookmark, false);
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter
->expects(static::once())
->method('format')
->with($bookmark)
->willReturn(['formatted' => $bookmark])
;
return $formatter;
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('delete_link', ['formatted' => $bookmark])
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete bookmark - Multiple bookmarks with valid parameters
*/
public function testDeleteMultipleBookmarks(): void
{
$parameters = ['id' => '123 456 789'];
$this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/?searchtags=abcdef';
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmarks = [
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'),
(new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456'),
(new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'),
];
$this->container->bookmarkService
->expects(static::exactly(3))
->method('get')
->withConsecutive([123], [456], [789])
->willReturnOnConsecutiveCalls(...$bookmarks)
;
$this->container->bookmarkService
->expects(static::exactly(3))
->method('remove')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark, false];
}, $bookmarks))
;
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmarks): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter
->expects(static::exactly(3))
->method('format')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark];
}, $bookmarks))
->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
return ['formatted' => $bookmark];
}, $bookmarks))
;
return $formatter;
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::exactly(3))
->method('executeHooks')
->with('delete_link')
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/?searchtags=abcdef'], $result->getHeader('location'));
}
/**
* Delete bookmark - Single bookmark not found in the data store
*/
public function testDeleteSingleBookmarkNotFound(): void
{
$parameters = ['id' => '123'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->willThrowException(new BookmarkNotFoundException())
;
$this->container->bookmarkService->expects(static::never())->method('remove');
$this->container->bookmarkService->expects(static::never())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function (): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter->expects(static::never())->method('format');
return $formatter;
})
;
// Make sure that PluginManager hook is not triggered
$this->container->pluginManager
->expects(static::never())
->method('executeHooks')
->with('delete_link')
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete bookmark - Multiple bookmarks with one not found in the data store
*/
public function testDeleteMultipleBookmarksOneNotFound(): void
{
$parameters = ['id' => '123 456 789'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$bookmarks = [
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'),
(new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'),
];
$this->container->bookmarkService
->expects(static::exactly(3))
->method('get')
->withConsecutive([123], [456], [789])
->willReturnCallback(function (int $id) use ($bookmarks): Bookmark {
if ($id === 123) {
return $bookmarks[0];
}
if ($id === 789) {
return $bookmarks[1];
}
throw new BookmarkNotFoundException();
})
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('remove')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark, false];
}, $bookmarks))
;
$this->container->bookmarkService->expects(static::once())->method('save');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->with('raw')
->willReturnCallback(function () use ($bookmarks): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter
->expects(static::exactly(2))
->method('format')
->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark];
}, $bookmarks))
->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
return ['formatted' => $bookmark];
}, $bookmarks))
;
return $formatter;
})
;
// Make sure that PluginManager hook is not triggered
$this->container->pluginManager
->expects(static::exactly(2))
->method('executeHooks')
->with('delete_link')
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.'])
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete bookmark - Invalid ID
*/
public function testDeleteInvalidId(): void
{
$parameters = ['id' => 'nope not an ID'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete bookmark - Empty ID
*/
public function testDeleteEmptyId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Delete bookmark - from bookmarklet
*/
public function testDeleteBookmarkFromBookmarklet(): void
{
$parameters = [
'id' => '123',
'source' => 'bookmarklet',
];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->bookmarkService->method('get')->with('123')->willReturn(
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
);
$this->container->bookmarkService->expects(static::once())->method('remove');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->willReturnCallback(function (): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter->method('format')->willReturn(['formatted']);
return $formatter;
})
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('<script>self.close();</script>', (string) $result->getBody());
}
/**
* Delete bookmark - from batch view
*/
public function testDeleteBookmarkFromBatch(): void
{
$parameters = [
'id' => '123',
'source' => 'batch',
];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->bookmarkService->method('get')->with('123')->willReturn(
(new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
);
$this->container->bookmarkService->expects(static::once())->method('remove');
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->expects(static::once())
->method('getFormatter')
->willReturnCallback(function (): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter->method('format')->willReturn(['formatted']);
return $formatter;
})
;
$result = $this->controller->deleteBookmark($request, $response);
static::assertSame(204, $result->getStatusCode());
static::assertEmpty((string) $result->getBody());
}
}

View file

@ -1,145 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaareManageController;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class PinBookmarkTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareManageController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareManageController($this->container);
}
/**
* Test pin bookmark - with valid input
*
* @dataProvider initialStickyValuesProvider()
*/
public function testPinBookmarkIsStickyNull(?bool $sticky, bool $expectedValue): void
{
$id = 123;
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)
->setUrl('http://domain.tld')
->setTitle('Title 123')
->setSticky($sticky)
;
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::once())
->method('executeHooks')
->with('save_link')
;
$result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
static::assertSame($expectedValue, $bookmark->isSticky());
}
public function initialStickyValuesProvider(): array
{
// [initialStickyState, isStickyAfterPin]
return [[null, true], [false, true], [true, false]];
}
/**
* Test pin bookmark - invalid bookmark ID
*/
public function testDisplayEditFormInvalidId(): void
{
$id = 'invalid';
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.'])
;
$result = $this->controller->pinBookmark($request, $response, ['id' => $id]);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test pin bookmark - Bookmark ID not provided
*/
public function testDisplayEditFormIdNotProvided(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.'])
;
$result = $this->controller->pinBookmark($request, $response, []);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test pin bookmark - bookmark not found
*/
public function testDisplayEditFormBookmarkNotFound(): void
{
$id = 123;
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willThrowException(new BookmarkNotFoundException())
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.'])
;
$result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
}

View file

@ -1,139 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaareManageController;
use Shaarli\Http\HttpAccess;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Test GET /admin/shaare/private/{hash}
*/
class SharePrivateTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaareManageController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaareManageController($this->container);
}
/**
* Test shaare private with a private bookmark which does not have a key yet.
*/
public function testSharePrivateWithNewPrivateBookmark(): void
{
$hash = 'abcdcef';
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)
->setUrl('http://domain.tld')
->setTitle('Title 123')
->setPrivate(true)
;
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willReturn($bookmark)
;
$this->container->bookmarkService
->expects(static::once())
->method('set')
->with($bookmark, true)
->willReturnCallback(function (Bookmark $bookmark): Bookmark {
static::assertSame(32, strlen($bookmark->getAdditionalContentEntry('private_key')));
return $bookmark;
})
;
$result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
static::assertSame(302, $result->getStatusCode());
static::assertRegExp('#/subfolder/shaare/' . $hash . '\?key=\w{32}#', $result->getHeaderLine('Location'));
}
/**
* Test shaare private with a private bookmark which does already have a key.
*/
public function testSharePrivateWithExistingPrivateBookmark(): void
{
$hash = 'abcdcef';
$existingKey = 'this is a private key';
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)
->setUrl('http://domain.tld')
->setTitle('Title 123')
->setPrivate(true)
->setAdditionalContentEntry('private_key', $existingKey)
;
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willReturn($bookmark)
;
$this->container->bookmarkService
->expects(static::never())
->method('set')
;
$result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/shaare/' . $hash . '?key=' . $existingKey, $result->getHeaderLine('Location'));
}
/**
* Test shaare private with a public bookmark.
*/
public function testSharePrivateWithPublicBookmark(): void
{
$hash = 'abcdcef';
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId(123)
->setUrl('http://domain.tld')
->setTitle('Title 123')
->setPrivate(false)
;
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willReturn($bookmark)
;
$this->container->bookmarkService
->expects(static::never())
->method('set')
;
$result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/shaare/' . $hash, $result->getHeaderLine('Location'));
}
}

View file

@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaarePublishController;
use Shaarli\Http\HttpAccess;
use Shaarli\Http\MetadataRetriever;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class DisplayCreateBatchFormTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaarePublishController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->container->metadataRetriever = $this->createMock(MetadataRetriever::class);
$this->controller = new ShaarePublishController($this->container);
}
/**
* TODO
*/
public function testDisplayCreateFormBatch(): void
{
$urls = [
'https://domain1.tld/url1',
'https://domain2.tld/url2',
' ',
'https://domain3.tld/url3',
];
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($urls): ?string {
return $key === 'urls' ? implode(PHP_EOL, $urls) : null;
});
$response = new Response();
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->displayCreateBatchForms($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink.batch', (string) $result->getBody());
static::assertTrue($assignedVariables['batch_mode']);
static::assertCount(3, $assignedVariables['links']);
static::assertSame($urls[0], $assignedVariables['links'][0]['link']['url']);
static::assertSame($urls[1], $assignedVariables['links'][1]['link']['url']);
static::assertSame($urls[3], $assignedVariables['links'][2]['link']['url']);
}
}

View file

@ -1,367 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaarePublishController;
use Shaarli\Http\HttpAccess;
use Shaarli\Http\MetadataRetriever;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class DisplayCreateFormTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaarePublishController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->container->metadataRetriever = $this->createMock(MetadataRetriever::class);
$this->controller = new ShaarePublishController($this->container);
}
/**
* Test displaying bookmark create form
* Ensure that every step of the standard workflow works properly.
*/
public function testDisplayCreateFormWithUrlAndWithMetadataRetrieval(): void
{
$this->container->environment = [
'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
];
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
$expectedUrl = str_replace('&utm_ad=pay', '', $url);
$remoteTitle = 'Remote Title';
$remoteDesc = 'Sometimes the meta description is relevant.';
$remoteTags = 'abc def';
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
return $key === 'post' ? $url : null;
});
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $param, $default) {
if ($param === 'general.enable_async_metadata') {
return false;
}
return $default;
});
$this->container->metadataRetriever->expects(static::once())->method('retrieve')->willReturn([
'title' => $remoteTitle,
'description' => $remoteDesc,
'tags' => $remoteTags,
]);
$this->container->bookmarkService
->expects(static::once())
->method('bookmarksCountPerTag')
->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_editlink'], ['render_includes'])
->willReturnCallback(function (string $hook, array $data) use ($remoteTitle, $remoteDesc): array {
if ('render_editlink' === $hook) {
static::assertSame($remoteTitle, $data['link']['title']);
static::assertSame($remoteDesc, $data['link']['description']);
}
return $data;
})
;
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
static::assertSame($expectedUrl, $assignedVariables['link']['url']);
static::assertSame($remoteTitle, $assignedVariables['link']['title']);
static::assertSame($remoteDesc, $assignedVariables['link']['description']);
static::assertSame($remoteTags . ' ', $assignedVariables['link']['tags']);
static::assertFalse($assignedVariables['link']['private']);
static::assertTrue($assignedVariables['link_is_new']);
static::assertSame($referer, $assignedVariables['http_referer']);
static::assertSame($tags, $assignedVariables['tags']);
static::assertArrayHasKey('source', $assignedVariables);
static::assertArrayHasKey('default_private_links', $assignedVariables);
static::assertArrayHasKey('async_metadata', $assignedVariables);
static::assertArrayHasKey('retrieve_description', $assignedVariables);
}
/**
* Test displaying bookmark create form without any external metadata retrieval attempt
*/
public function testDisplayCreateFormWithUrlAndWithoutMetadata(): void
{
$this->container->environment = [
'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
];
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
$expectedUrl = str_replace('&utm_ad=pay', '', $url);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
return $key === 'post' ? $url : null;
});
$response = new Response();
$this->container->metadataRetriever->expects(static::never())->method('retrieve');
$this->container->bookmarkService
->expects(static::once())
->method('bookmarksCountPerTag')
->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_editlink'], ['render_includes'])
->willReturnCallback(function (string $hook, array $data): array {
if ('render_editlink' === $hook) {
static::assertSame('', $data['link']['title']);
static::assertSame('', $data['link']['description']);
}
return $data;
})
;
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
static::assertSame($expectedUrl, $assignedVariables['link']['url']);
static::assertSame('', $assignedVariables['link']['title']);
static::assertSame('', $assignedVariables['link']['description']);
static::assertSame('', $assignedVariables['link']['tags']);
static::assertFalse($assignedVariables['link']['private']);
static::assertTrue($assignedVariables['link_is_new']);
static::assertSame($referer, $assignedVariables['http_referer']);
static::assertSame($tags, $assignedVariables['tags']);
static::assertArrayHasKey('source', $assignedVariables);
static::assertArrayHasKey('default_private_links', $assignedVariables);
static::assertArrayHasKey('async_metadata', $assignedVariables);
static::assertArrayHasKey('retrieve_description', $assignedVariables);
}
/**
* Test displaying bookmark create form
* Ensure all available query parameters are handled properly.
*/
public function testDisplayCreateFormWithFullParameters(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$parameters = [
'post' => 'http://url.tld/other?part=3&utm_ad=pay#hash',
'title' => 'Provided Title',
'description' => 'Provided description.',
'tags' => 'abc@def',
'private' => '1',
'source' => 'apps',
];
$expectedUrl = str_replace('&utm_ad=pay', '', $parameters['post']);
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
});
$response = new Response();
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
static::assertSame($expectedUrl, $assignedVariables['link']['url']);
static::assertSame($parameters['title'], $assignedVariables['link']['title']);
static::assertSame($parameters['description'], $assignedVariables['link']['description']);
static::assertSame($parameters['tags'] . '@', $assignedVariables['link']['tags']);
static::assertTrue($assignedVariables['link']['private']);
static::assertTrue($assignedVariables['link_is_new']);
static::assertSame($parameters['source'], $assignedVariables['source']);
}
/**
* Test displaying bookmark create form
* Without any parameter.
*/
public function testDisplayCreateFormEmpty(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->httpAccess->expects(static::never())->method('getHttpResponse');
$this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('', $assignedVariables['link']['url']);
static::assertSame('Note: ', $assignedVariables['link']['title']);
static::assertSame('', $assignedVariables['link']['description']);
static::assertSame('', $assignedVariables['link']['tags']);
static::assertFalse($assignedVariables['link']['private']);
static::assertTrue($assignedVariables['link_is_new']);
}
/**
* Test displaying bookmark create form
* URL not using HTTP protocol: do not try to retrieve the title
*/
public function testDisplayCreateFormNotHttp(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$url = 'magnet://kubuntu.torrent';
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($url): ?string {
return $key === 'post' ? $url : null;
});
$response = new Response();
$this->container->httpAccess->expects(static::never())->method('getHttpResponse');
$this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame($url, $assignedVariables['link']['url']);
static::assertTrue($assignedVariables['link_is_new']);
}
/**
* Test displaying bookmark create form
* When markdown formatter is enabled, the no markdown tag should be added to existing tags.
*/
public function testDisplayCreateFormWithMarkdownEnabled(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->expects(static::atLeastOnce())
->method('get')->willReturnCallback(function (string $key): ?string {
if ($key === 'formatter') {
return 'markdown';
}
return $key;
})
;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame(['nomarkdown' => 1], $assignedVariables['tags']);
}
/**
* Test displaying bookmark create form
* When an existing URL is submitted, we want to edit the existing link.
*/
public function testDisplayCreateFormWithExistingUrl(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
$expectedUrl = str_replace('&utm_ad=pay', '', $url);
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($url): ?string {
return $key === 'post' ? $url : null;
});
$response = new Response();
$this->container->httpAccess->expects(static::never())->method('getHttpResponse');
$this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
$this->container->bookmarkService
->expects(static::once())
->method('findByUrl')
->with($expectedUrl)
->willReturn(
(new Bookmark())
->setId($id = 23)
->setUrl($expectedUrl)
->setTitle($title = 'Bookmark Title')
->setDescription($description = 'Bookmark description.')
->setTags($tags = ['abc', 'def'])
->setPrivate(true)
->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44'))
)
;
$result = $this->controller->displayCreateForm($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
static::assertFalse($assignedVariables['link_is_new']);
static::assertSame($id, $assignedVariables['link']['id']);
static::assertSame($expectedUrl, $assignedVariables['link']['url']);
static::assertSame($title, $assignedVariables['link']['title']);
static::assertSame($description, $assignedVariables['link']['description']);
static::assertSame(implode('@', $tags) . '@', $assignedVariables['link']['tags']);
static::assertTrue($assignedVariables['link']['private']);
static::assertSame($createdAt, $assignedVariables['link']['created']);
}
}

View file

@ -1,155 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaarePublishController;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class DisplayEditFormTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaarePublishController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaarePublishController($this->container);
}
/**
* Test displaying bookmark edit form
* When an existing ID is provided, ensure that default workflow works properly.
*/
public function testDisplayEditFormDefault(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$id = 11;
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->httpAccess->expects(static::never())->method('getHttpResponse');
$this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willReturn(
(new Bookmark())
->setId($id)
->setUrl($url = 'http://domain.tld')
->setTitle($title = 'Bookmark Title')
->setDescription($description = 'Bookmark description.')
->setTags($tags = ['abc', 'def'])
->setPrivate(true)
->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44'))
)
;
$result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('editlink', (string) $result->getBody());
static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
static::assertFalse($assignedVariables['link_is_new']);
static::assertSame($id, $assignedVariables['link']['id']);
static::assertSame($url, $assignedVariables['link']['url']);
static::assertSame($title, $assignedVariables['link']['title']);
static::assertSame($description, $assignedVariables['link']['description']);
static::assertSame(implode('@', $tags) . '@', $assignedVariables['link']['tags']);
static::assertTrue($assignedVariables['link']['private']);
static::assertSame($createdAt, $assignedVariables['link']['created']);
}
/**
* Test displaying bookmark edit form
* Invalid ID provided.
*/
public function testDisplayEditFormInvalidId(): void
{
$id = 'invalid';
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.'])
;
$result = $this->controller->displayEditForm($request, $response, ['id' => $id]);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test displaying bookmark edit form
* ID not provided.
*/
public function testDisplayEditFormIdNotProvided(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.'])
;
$result = $this->controller->displayEditForm($request, $response, []);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
/**
* Test displaying bookmark edit form
* Bookmark not found.
*/
public function testDisplayEditFormBookmarkNotFound(): void
{
$id = 123;
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willThrowException(new BookmarkNotFoundException())
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.'])
;
$result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/subfolder/'], $result->getHeader('location'));
}
}

View file

@ -1,368 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Front\Controller\Admin\ShaarePublishController;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Http\HttpAccess;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class SaveBookmarkTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaarePublishController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->httpAccess = $this->createMock(HttpAccess::class);
$this->controller = new ShaarePublishController($this->container);
}
/**
* Test save a new bookmark
*/
public function testSaveBookmark(): void
{
$id = 21;
$parameters = [
'lf_url' => 'http://url.tld/other?part=3#hash',
'lf_title' => 'Provided Title',
'lf_description' => 'Provided description.',
'lf_tags' => 'abc def',
'lf_private' => '1',
'returnurl' => 'http://shaarli/subfolder/admin/add-shaare'
];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$checkBookmark = function (Bookmark $bookmark) use ($parameters) {
static::assertSame($parameters['lf_url'], $bookmark->getUrl());
static::assertSame($parameters['lf_title'], $bookmark->getTitle());
static::assertSame($parameters['lf_description'], $bookmark->getDescription());
static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
static::assertTrue($bookmark->isPrivate());
};
$this->container->bookmarkService
->expects(static::once())
->method('addOrSet')
->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
static::assertFalse($save);
$checkBookmark($bookmark);
$bookmark->setId($id);
return $bookmark;
})
;
$this->container->bookmarkService
->expects(static::once())
->method('set')
->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
static::assertTrue($save);
$checkBookmark($bookmark);
static::assertSame($id, $bookmark->getId());
return $bookmark;
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['save_link'])
->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
if ('save_link' === $hook) {
static::assertSame($id, $data['id']);
static::assertSame($parameters['lf_url'], $data['url']);
static::assertSame($parameters['lf_title'], $data['title']);
static::assertSame($parameters['lf_description'], $data['description']);
static::assertSame($parameters['lf_tags'], $data['tags']);
static::assertTrue($data['private']);
}
return $data;
})
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertRegExp('@/subfolder/#[\w\-]{6}@', $result->getHeader('location')[0]);
}
/**
* Test save an existing bookmark
*/
public function testSaveExistingBookmark(): void
{
$id = 21;
$parameters = [
'lf_id' => (string) $id,
'lf_url' => 'http://url.tld/other?part=3#hash',
'lf_title' => 'Provided Title',
'lf_description' => 'Provided description.',
'lf_tags' => 'abc def',
'lf_private' => '1',
'returnurl' => 'http://shaarli/subfolder/?page=2'
];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$checkBookmark = function (Bookmark $bookmark) use ($parameters, $id) {
static::assertSame($id, $bookmark->getId());
static::assertSame($parameters['lf_url'], $bookmark->getUrl());
static::assertSame($parameters['lf_title'], $bookmark->getTitle());
static::assertSame($parameters['lf_description'], $bookmark->getDescription());
static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
static::assertTrue($bookmark->isPrivate());
};
$this->container->bookmarkService->expects(static::atLeastOnce())->method('exists')->willReturn(true);
$this->container->bookmarkService
->expects(static::once())
->method('get')
->willReturn((new Bookmark())->setId($id)->setUrl('http://other.url'))
;
$this->container->bookmarkService
->expects(static::once())
->method('addOrSet')
->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
static::assertFalse($save);
$checkBookmark($bookmark);
return $bookmark;
})
;
$this->container->bookmarkService
->expects(static::once())
->method('set')
->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
static::assertTrue($save);
$checkBookmark($bookmark);
static::assertSame($id, $bookmark->getId());
return $bookmark;
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['save_link'])
->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
if ('save_link' === $hook) {
static::assertSame($id, $data['id']);
static::assertSame($parameters['lf_url'], $data['url']);
static::assertSame($parameters['lf_title'], $data['title']);
static::assertSame($parameters['lf_description'], $data['description']);
static::assertSame($parameters['lf_tags'], $data['tags']);
static::assertTrue($data['private']);
}
return $data;
})
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertRegExp('@/subfolder/\?page=2#[\w\-]{6}@', $result->getHeader('location')[0]);
}
/**
* Test save a bookmark - try to retrieve the thumbnail
*/
public function testSaveBookmarkWithThumbnailSync(): void
{
$parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ($key === 'thumbnails.mode') {
return Thumbnailer::MODE_ALL;
} elseif ($key === 'general.enable_async_metadata') {
return false;
}
return $default;
});
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer
->expects(static::once())
->method('get')
->with($parameters['lf_url'])
->willReturn($thumb = 'http://thumb.url')
;
$this->container->bookmarkService
->expects(static::once())
->method('addOrSet')
->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): Bookmark {
static::assertSame($thumb, $bookmark->getThumbnail());
return $bookmark;
})
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
}
/**
* Test save a bookmark - with ID #0
*/
public function testSaveBookmarkWithIdZero(): void
{
$parameters = ['lf_id' => '0'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->bookmarkService->expects(static::once())->method('exists')->with(0)->willReturn(true);
$this->container->bookmarkService->expects(static::once())->method('get')->with(0)->willReturn(new Bookmark());
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
}
/**
* Test save a bookmark - do not attempt to retrieve thumbnails if async mode is enabled.
*/
public function testSaveBookmarkWithThumbnailAsync(): void
{
$parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ($key === 'thumbnails.mode') {
return Thumbnailer::MODE_ALL;
} elseif ($key === 'general.enable_async_metadata') {
return true;
}
return $default;
});
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer->expects(static::never())->method('get');
$this->container->bookmarkService
->expects(static::once())
->method('addOrSet')
->willReturnCallback(function (Bookmark $bookmark): Bookmark {
static::assertNull($bookmark->getThumbnail());
return $bookmark;
})
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
}
/**
* Change the password with a wrong existing password
*/
public function testSaveBookmarkFromBookmarklet(): void
{
$parameters = ['source' => 'bookmarklet'];
$request = $this->createMock(Request::class);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($parameters): ?string {
return $parameters[$key] ?? null;
})
;
$response = new Response();
$result = $this->controller->save($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('<script>self.close();</script>', (string) $result->getBody());
}
/**
* Change the password with a wrong existing password
*/
public function testSaveBookmarkWrongToken(): void
{
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->willReturn(false);
$this->container->bookmarkService->expects(static::never())->method('addOrSet');
$this->container->bookmarkService->expects(static::never())->method('set');
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(WrongTokenException::class);
$this->controller->save($request, $response);
}
}

View file

@ -1,184 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
/**
* Class ShaarliControllerTest
*
* This class is used to test default behavior of ShaarliAdminController abstract class.
* It uses a dummy non abstract controller.
*/
class ShaarliAdminControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ShaarliAdminController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new class ($this->container) extends ShaarliAdminController
{
public function checkToken(Request $request): bool
{
return parent::checkToken($request);
}
public function saveSuccessMessage(string $message): void
{
parent::saveSuccessMessage($message);
}
public function saveWarningMessage(string $message): void
{
parent::saveWarningMessage($message);
}
public function saveErrorMessage(string $message): void
{
parent::saveErrorMessage($message);
}
};
}
/**
* Trigger controller's checkToken with a valid token.
*/
public function testCheckTokenWithValidToken(): void
{
$request = $this->createMock(Request::class);
$request->method('getParam')->with('token')->willReturn($token = '12345');
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->with($token)->willReturn(true);
static::assertTrue($this->controller->checkToken($request));
}
/**
* Trigger controller's checkToken with na valid token should raise an exception.
*/
public function testCheckTokenWithNotValidToken(): void
{
$request = $this->createMock(Request::class);
$request->method('getParam')->with('token')->willReturn($token = '12345');
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager->method('checkToken')->with($token)->willReturn(false);
$this->expectException(WrongTokenException::class);
$this->controller->checkToken($request);
}
/**
* Test saveSuccessMessage() with a first message.
*/
public function testSaveSuccessMessage(): void
{
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES, [$message = 'bravo!'])
;
$this->controller->saveSuccessMessage($message);
}
/**
* Test saveSuccessMessage() with existing messages.
*/
public function testSaveSuccessMessageWithExistingMessages(): void
{
$this->container->sessionManager
->expects(static::once())
->method('getSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES)
->willReturn(['success1', 'success2'])
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES, ['success1', 'success2', $message = 'bravo!'])
;
$this->controller->saveSuccessMessage($message);
}
/**
* Test saveWarningMessage() with a first message.
*/
public function testSaveWarningMessage(): void
{
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_WARNING_MESSAGES, [$message = 'warning!'])
;
$this->controller->saveWarningMessage($message);
}
/**
* Test saveWarningMessage() with existing messages.
*/
public function testSaveWarningMessageWithExistingMessages(): void
{
$this->container->sessionManager
->expects(static::once())
->method('getSessionParameter')
->with(SessionManager::KEY_WARNING_MESSAGES)
->willReturn(['warning1', 'warning2'])
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_WARNING_MESSAGES, ['warning1', 'warning2', $message = 'warning!'])
;
$this->controller->saveWarningMessage($message);
}
/**
* Test saveErrorMessage() with a first message.
*/
public function testSaveErrorMessage(): void
{
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, [$message = 'error!'])
;
$this->controller->saveErrorMessage($message);
}
/**
* Test saveErrorMessage() with existing messages.
*/
public function testSaveErrorMessageWithExistingMessages(): void
{
$this->container->sessionManager
->expects(static::once())
->method('getSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES)
->willReturn(['error1', 'error2'])
;
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_ERROR_MESSAGES, ['error1', 'error2', $message = 'error!'])
;
$this->controller->saveErrorMessage($message);
}
}

View file

@ -1,157 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Bookmark\SearchResult;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class ThumbnailsControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ThumbnailsController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ThumbnailsController($this->container);
}
/**
* Test displaying the thumbnails update page
* Note that only non-note and HTTP bookmarks should be returned.
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->willReturn(SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('?abcdef')->setTitle('Note 1'),
(new Bookmark())->setId(3)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(4)->setUrl('ftp://domain.tld', ['ftp'])->setTitle('FTP'),
]))
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('thumbnails', (string) $result->getBody());
static::assertSame('Thumbnails update - Shaarli', $assignedVariables['pagetitle']);
static::assertSame([1, 3], $assignedVariables['ids']);
}
/**
* Test updating a bookmark thumbnail with valid parameters
*/
public function testAjaxUpdateValid(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId($id = 123)
->setUrl($url = 'http://url1.tld')
->setTitle('Title 1')
->setThumbnail(false)
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer
->expects(static::once())
->method('get')
->with($url)
->willReturn($thumb = 'http://img.tld/pic.png')
;
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willReturn($bookmark)
;
$this->container->bookmarkService
->expects(static::once())
->method('set')
->willReturnCallback(function (Bookmark $bookmark) use ($thumb): Bookmark {
static::assertSame($thumb, $bookmark->getThumbnail());
return $bookmark;
})
;
$result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
static::assertSame(200, $result->getStatusCode());
$payload = json_decode((string) $result->getBody(), true);
static::assertSame($id, $payload['id']);
static::assertSame($url, $payload['url']);
static::assertSame($thumb, $payload['thumbnail']);
}
/**
* Test updating a bookmark thumbnail - Invalid ID
*/
public function testAjaxUpdateInvalidId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->ajaxUpdate($request, $response, ['id' => 'nope']);
static::assertSame(400, $result->getStatusCode());
}
/**
* Test updating a bookmark thumbnail - No ID
*/
public function testAjaxUpdateNoId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->ajaxUpdate($request, $response, []);
static::assertSame(400, $result->getStatusCode());
}
/**
* Test updating a bookmark thumbnail with valid parameters
*/
public function testAjaxUpdateBookmarkNotFound(): void
{
$id = 123;
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willThrowException(new BookmarkNotFoundException())
;
$result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
static::assertSame(404, $result->getStatusCode());
}
}

View file

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class TokenControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var TokenController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new TokenController($this->container);
}
public function testGetToken(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager
->expects(static::once())
->method('generateToken')
->willReturn($token = 'token1234')
;
$result = $this->controller->getToken($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame($token, (string) $result->getBody());
}
}

View file

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ToolsControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ToolsController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ToolsController($this->container);
}
public function testDefaultInvokeWithHttps(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->environment = [
'SERVER_NAME' => 'shaarli',
'SERVER_PORT' => 443,
'HTTPS' => 'on',
];
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('tools', (string) $result->getBody());
static::assertSame('https://shaarli/', $assignedVariables['pageabsaddr']);
static::assertTrue($assignedVariables['sslenabled']);
}
public function testDefaultInvokeWithoutHttps(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->environment = [
'SERVER_NAME' => 'shaarli',
'SERVER_PORT' => 80,
];
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('tools', (string) $result->getBody());
static::assertSame('http://shaarli/', $assignedVariables['pageabsaddr']);
static::assertFalse($assignedVariables['sslenabled']);
}
}

View file

@ -1,538 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Bookmark\SearchResult;
use Shaarli\Config\ConfigManager;
use Shaarli\Security\LoginManager;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class BookmarkListControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var BookmarkListController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new BookmarkListController($this->container);
}
/**
* Test rendering list of bookmarks with default parameters (first page).
*/
public function testIndexDefaultFirstPage(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => '', 'searchterm' => ''],
null,
false,
false,
false,
['offset' => 0, 'limit' => 2]
)
->willReturn(SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
], 0, 2));
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $parameter, $default = null) {
if ('LINKS_PER_PAGE' === $parameter) {
return 2;
}
return $default;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['pagetitle']);
static::assertSame('?page=2', $assignedVariables['previous_page_url']);
static::assertSame('', $assignedVariables['next_page_url']);
static::assertSame(2, $assignedVariables['page_max']);
static::assertSame('', $assignedVariables['search_tags']);
static::assertSame(3, $assignedVariables['result_count']);
static::assertSame(1, $assignedVariables['page_current']);
static::assertSame('', $assignedVariables['search_term']);
static::assertNull($assignedVariables['visibility']);
static::assertCount(2, $assignedVariables['links']);
$link = $assignedVariables['links'][0];
static::assertSame(1, $link['id']);
static::assertSame('http://url1.tld', $link['url']);
static::assertSame('Title 1', $link['title']);
$link = $assignedVariables['links'][1];
static::assertSame(2, $link['id']);
static::assertSame('http://url2.tld', $link['url']);
static::assertSame('Title 2', $link['title']);
}
/**
* Test rendering list of bookmarks with default parameters (second page).
*/
public function testIndexDefaultSecondPage(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) {
if ('page' === $key) {
return '2';
}
return null;
});
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => '', 'searchterm' => ''],
null,
false,
false,
false,
['offset' => 2, 'limit' => 2]
)
->willReturn(SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
], 2, 2))
;
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $parameter, $default = null) {
if ('LINKS_PER_PAGE' === $parameter) {
return 2;
}
return $default;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['pagetitle']);
static::assertSame('', $assignedVariables['previous_page_url']);
static::assertSame('?page=1', $assignedVariables['next_page_url']);
static::assertSame(2, $assignedVariables['page_max']);
static::assertSame('', $assignedVariables['search_tags']);
static::assertSame(3, $assignedVariables['result_count']);
static::assertSame(2, $assignedVariables['page_current']);
static::assertSame('', $assignedVariables['search_term']);
static::assertNull($assignedVariables['visibility']);
static::assertCount(1, $assignedVariables['links']);
$link = $assignedVariables['links'][2];
static::assertSame(3, $link['id']);
static::assertSame('http://url3.tld', $link['url']);
static::assertSame('Title 3', $link['title']);
}
/**
* Test rendering list of bookmarks with filters.
*/
public function testIndexDefaultWithFilters(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) {
if ('searchtags' === $key) {
return 'abc@def';
}
if ('searchterm' === $key) {
return 'ghi jkl';
}
return null;
});
$response = new Response();
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $key, $default) {
if ('LINKS_PER_PAGE' === $key) {
return 2;
}
if ('visibility' === $key) {
return 'private';
}
if ('untaggedonly' === $key) {
return true;
}
return $default;
})
;
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => 'abc@def', 'searchterm' => 'ghi jkl'],
'private',
false,
true,
false,
['offset' => 0, 'limit' => 2]
)
->willReturn(SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
], 0, 2))
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Search: ghi jkl [abc] [def] - Shaarli', $assignedVariables['pagetitle']);
static::assertSame('?page=2&searchterm=ghi+jkl&searchtags=abc%40def', $assignedVariables['previous_page_url']);
}
/**
* Test displaying a permalink with valid parameters
*/
public function testPermalinkValid(): void
{
$hash = 'abcdef';
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
;
$result = $this->controller->permalink($request, $response, ['hash' => $hash]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Title 1 - Shaarli', $assignedVariables['pagetitle']);
static::assertCount(1, $assignedVariables['links']);
$link = $assignedVariables['links'][0];
static::assertSame(123, $link['id']);
static::assertSame('http://url1.tld', $link['url']);
static::assertSame('Title 1', $link['title']);
}
/**
* Test displaying a permalink with an unknown small hash : renders a 404 template error
*/
public function testPermalinkNotFound(): void
{
$hash = 'abcdef';
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willThrowException(new BookmarkNotFoundException())
;
$result = $this->controller->permalink($request, $response, ['hash' => $hash]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('404', (string) $result->getBody());
static::assertSame(
'The link you are trying to reach does not exist or has been deleted.',
$assignedVariables['error_message']
);
}
/**
* Test GET /shaare/{hash}?key={key} - Find a link by hash using a private link.
*/
public function testPermalinkWithPrivateKey(): void
{
$hash = 'abcdef';
$privateKey = 'this is a private key';
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key, $default = null) use ($privateKey) {
return $key === 'key' ? $privateKey : $default;
});
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash, $privateKey)
->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
;
$result = $this->controller->permalink($request, $response, ['hash' => $hash]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertCount(1, $assignedVariables['links']);
}
/**
* Test getting link list with thumbnail updates.
* -> 2 thumbnails update, only 1 datastore write
*/
public function testThumbnailUpdateFromLinkList(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $default) {
if ($key === 'thumbnails.mode') {
return Thumbnailer::MODE_ALL;
} elseif ($key === 'general.enable_async_metadata') {
return false;
}
return $default;
})
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer
->expects(static::exactly(2))
->method('get')
->withConsecutive(['https://url2.tld'], ['https://url4.tld'])
;
$this->container->bookmarkService
->expects(static::once())
->method('search')
->willReturn(SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false),
$b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false),
$b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'),
(new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'),
]))
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('set')
->withConsecutive([$b1, false], [$b2, false])
;
$this->container->bookmarkService->expects(static::once())->method('save');
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Test getting a permalink with thumbnail update.
*/
public function testThumbnailUpdateFromPermalink(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $default) {
if ($key === 'thumbnails.mode') {
return Thumbnailer::MODE_ALL;
} elseif ($key === 'general.enable_async_metadata') {
return false;
}
return $default;
})
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']);
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
;
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
$this->container->bookmarkService->expects(static::never())->method('save');
$result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Test getting a permalink with thumbnail update with async setting: no update should run.
*/
public function testThumbnailUpdateFromPermalinkAsync(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $default) {
if ($key === 'thumbnails.mode') {
return Thumbnailer::MODE_ALL;
} elseif ($key === 'general.enable_async_metadata') {
return true;
}
return $default;
})
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer->expects(static::never())->method('get');
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->willReturn((new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
;
$this->container->bookmarkService->expects(static::never())->method('set');
$this->container->bookmarkService->expects(static::never())->method('save');
$result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
static::assertSame(200, $result->getStatusCode());
}
/**
* Trigger legacy controller in link list controller: permalink
*/
public function testLegacyControllerPermalink(): void
{
$hash = 'abcdef';
$this->container->environment['QUERY_STRING'] = $hash;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]);
}
/**
* Trigger legacy controller in link list controller: ?do= query parameter
*/
public function testLegacyControllerDoPage(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('do')->willReturn('picwall');
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]);
}
/**
* Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route
*/
public function testLegacyControllerUnknownDoPage(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('do')->willReturn('nope');
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Trigger legacy controller in link list controller: other GET route (e.g. ?post)
*/
public function testLegacyControllerGetParameter(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(
'/subfolder/admin/shaare?post=' . urlencode($url),
$result->getHeader('location')[0]
);
}
}

View file

@ -1,726 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\SearchResult;
use Shaarli\Feed\CachedPage;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class DailyControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var DailyController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new DailyController($this->container);
DailyController::$DAILY_RSS_NB_DAYS = 2;
}
public function testValidIndexControllerInvokeDefault(): void
{
$currentDay = new \DateTimeImmutable('2020-05-13');
$previousDate = new \DateTime('2 days ago 00:00:00');
$nextDate = new \DateTime('today 00:00:00');
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
return $key === 'day' ? $currentDay->format('Ymd') : null;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(
function ($from, $to, &$previous, &$next) use ($currentDay, $previousDate, $nextDate): array {
$previous = $previousDate;
$next = $nextDate;
return [
(new Bookmark())
->setId(1)
->setUrl('http://url.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
(new Bookmark())
->setId(2)
->setUrl('http://url2.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
(new Bookmark())
->setId(3)
->setUrl('http://url3.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
];
}
)
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_daily'])
->willReturnCallback(
function (string $hook, array $data, array $param) use ($currentDay, $previousDate, $nextDate): array {
if ('render_daily' === $hook) {
static::assertArrayHasKey('linksToDisplay', $data);
static::assertCount(3, $data['linksToDisplay']);
static::assertSame(1, $data['linksToDisplay'][0]['id']);
static::assertSame($currentDay->getTimestamp(), $data['day']);
static::assertSame($previousDate->format('Ymd'), $data['previousday']);
static::assertSame($nextDate->format('Ymd'), $data['nextday']);
static::assertArrayHasKey('loggedin', $param);
}
return $data;
}
)
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertSame(
'Daily - ' . format_date($currentDay, false, true) . ' - Shaarli',
$assignedVariables['pagetitle']
);
static::assertEquals($currentDay, $assignedVariables['dayDate']);
static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
static::assertSame($previousDate->format('Ymd'), $assignedVariables['previousday']);
static::assertSame($nextDate->format('Ymd'), $assignedVariables['nextday']);
static::assertSame('day', $assignedVariables['type']);
static::assertSame('May 13, 2020', $assignedVariables['dayDesc']);
static::assertSame('Daily', $assignedVariables['localizedType']);
static::assertCount(3, $assignedVariables['linksToDisplay']);
$link = $assignedVariables['linksToDisplay'][0];
static::assertSame(1, $link['id']);
static::assertSame('http://url.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
$link = $assignedVariables['linksToDisplay'][1];
static::assertSame(2, $link['id']);
static::assertSame('http://url2.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
$link = $assignedVariables['linksToDisplay'][2];
static::assertSame(3, $link['id']);
static::assertSame('http://url3.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
static::assertCount(3, $assignedVariables['cols']);
static::assertCount(1, $assignedVariables['cols'][0]);
static::assertCount(1, $assignedVariables['cols'][1]);
static::assertCount(1, $assignedVariables['cols'][2]);
$link = $assignedVariables['cols'][0][0];
static::assertSame(1, $link['id']);
static::assertSame('http://url.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
$link = $assignedVariables['cols'][1][0];
static::assertSame(2, $link['id']);
static::assertSame('http://url2.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
$link = $assignedVariables['cols'][2][0];
static::assertSame(3, $link['id']);
static::assertSame('http://url3.tld', $link['url']);
static::assertNotEmpty($link['title']);
static::assertNotEmpty($link['description']);
static::assertNotEmpty($link['formatedDescription']);
}
/**
* Daily page - test that everything goes fine with no future or past bookmarks
*/
public function testValidIndexControllerInvokeNoFutureOrPast(): void
{
$currentDay = new \DateTimeImmutable('2020-05-13');
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
return $key === 'day' ? $currentDay->format('Ymd') : null;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(function () use ($currentDay): array {
return [
(new Bookmark())
->setId(1)
->setUrl('http://url.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
];
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_daily'])
->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
if ('render_daily' === $hook) {
static::assertArrayHasKey('linksToDisplay', $data);
static::assertCount(1, $data['linksToDisplay']);
static::assertSame(1, $data['linksToDisplay'][0]['id']);
static::assertSame($currentDay->getTimestamp(), $data['day']);
static::assertEmpty($data['previousday']);
static::assertEmpty($data['nextday']);
static::assertArrayHasKey('loggedin', $param);
}
return $data;
});
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertSame(
'Daily - ' . format_date($currentDay, false, true) . ' - Shaarli',
$assignedVariables['pagetitle']
);
static::assertCount(1, $assignedVariables['linksToDisplay']);
$link = $assignedVariables['linksToDisplay'][0];
static::assertSame(1, $link['id']);
}
/**
* Daily page - test that height adjustment in columns is working
*/
public function testValidIndexControllerInvokeHeightAdjustment(): void
{
$currentDay = new \DateTimeImmutable('2020-05-13');
$request = $this->createMock(Request::class);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(function () use ($currentDay): array {
return [
(new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
(new Bookmark())
->setId(2)
->setUrl('http://url.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(5000))
,
(new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'),
(new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'),
(new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'),
(new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'),
(new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'),
];
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->willReturnCallback(function (string $hook, array $data, array $param): array {
return $data;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertCount(7, $assignedVariables['linksToDisplay']);
$columnIds = function (array $column): array {
return array_map(function (array $item): int {
return $item['id'];
}, $column);
};
static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0]));
static::assertSame([2], $columnIds($assignedVariables['cols'][1]));
static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2]));
}
/**
* Daily page - no bookmark
*/
public function testValidIndexControllerInvokeNoBookmark(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
// Links dataset: 2 links with thumbnails
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(function (): array {
return [];
})
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->willReturnCallback(function (string $hook, array $data, array $param): array {
return $data;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertCount(0, $assignedVariables['linksToDisplay']);
static::assertSame('Today - ' . (new \DateTime())->format('F j, Y'), $assignedVariables['dayDesc']);
static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
}
/**
* Daily RSS - default behaviour
*/
public function testValidRssControllerInvokeDefault(): void
{
$dates = [
new \DateTimeImmutable('2020-05-17'),
new \DateTimeImmutable('2020-05-15'),
new \DateTimeImmutable('2020-05-13'),
new \DateTimeImmutable('+1 month'),
];
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService->expects(static::once())->method('search')->willReturn(
SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
(new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
(new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
(new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
(new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'),
])
);
$this->container->pageCacheManager
->expects(static::once())
->method('getCachePage')
->willReturnCallback(function (): CachedPage {
$cachedPage = $this->createMock(CachedPage::class);
$cachedPage->expects(static::once())->method('cache')->with('dailyrss');
return $cachedPage;
});
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(3, $assignedVariables['days']);
$day = $assignedVariables['days'][$dates[0]->format('Ymd')];
$date = $dates[0]->setTime(23, 59, 59);
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame(format_date($date, false), $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[0]->format('Ymd'), $day['absolute_url']);
static::assertCount(1, $day['links']);
static::assertSame(1, $day['links'][0]['id']);
static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
static::assertEquals($dates[0], $day['links'][0]['created']);
$day = $assignedVariables['days'][$dates[1]->format('Ymd')];
$date = $dates[1]->setTime(23, 59, 59);
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame(format_date($date, false), $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[1]->format('Ymd'), $day['absolute_url']);
static::assertCount(2, $day['links']);
static::assertSame(2, $day['links'][0]['id']);
static::assertSame('http://domain.tld/2', $day['links'][0]['url']);
static::assertEquals($dates[1], $day['links'][0]['created']);
static::assertSame(3, $day['links'][1]['id']);
static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
static::assertEquals($dates[1], $day['links'][1]['created']);
$day = $assignedVariables['days'][$dates[2]->format('Ymd')];
$date = $dates[2]->setTime(23, 59, 59);
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame(format_date($date, false), $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?day=' . $dates[2]->format('Ymd'), $day['absolute_url']);
static::assertCount(1, $day['links']);
static::assertSame(4, $day['links'][0]['id']);
static::assertSame('http://domain.tld/4', $day['links'][0]['url']);
static::assertEquals($dates[2], $day['links'][0]['created']);
}
/**
* Daily RSS - trigger cache rendering
*/
public function testValidRssControllerInvokeTriggerCache(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->pageCacheManager->method('getCachePage')->willReturnCallback(function (): CachedPage {
$cachedPage = $this->createMock(CachedPage::class);
$cachedPage->method('cachedVersion')->willReturn('this is cache!');
return $cachedPage;
});
$this->container->bookmarkService->expects(static::never())->method('search');
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('this is cache!', (string) $result->getBody());
}
/**
* Daily RSS - No bookmark
*/
public function testValidRssControllerInvokeNoBookmark(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())->method('search')
->willReturn(SearchResult::getSearchResult([]));
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(0, $assignedVariables['days']);
}
/**
* Test simple display index with week parameter
*/
public function testSimpleIndexWeekly(): void
{
$currentDay = new \DateTimeImmutable('2020-05-13');
$expectedDay = new \DateTimeImmutable('2020-05-11');
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
return $key === 'week' ? $currentDay->format('YW') : null;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(
function (): array {
return [
(new Bookmark())
->setId(1)
->setUrl('http://url.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
(new Bookmark())
->setId(2)
->setUrl('http://url2.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
];
}
)
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertSame(
'Weekly - Week 20 (May 11, 2020) - Shaarli',
$assignedVariables['pagetitle']
);
static::assertCount(2, $assignedVariables['linksToDisplay']);
static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
static::assertSame('', $assignedVariables['previousday']);
static::assertSame('', $assignedVariables['nextday']);
static::assertSame('Week 20 (May 11, 2020)', $assignedVariables['dayDesc']);
static::assertSame('week', $assignedVariables['type']);
static::assertSame('Weekly', $assignedVariables['localizedType']);
}
/**
* Test simple display index with month parameter
*/
public function testSimpleIndexMonthly(): void
{
$currentDay = new \DateTimeImmutable('2020-05-13');
$expectedDay = new \DateTimeImmutable('2020-05-01');
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
return $key === 'month' ? $currentDay->format('Ym') : null;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->bookmarkService
->expects(static::once())
->method('findByDate')
->willReturnCallback(
function (): array {
return [
(new Bookmark())
->setId(1)
->setUrl('http://url.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
(new Bookmark())
->setId(2)
->setUrl('http://url2.tld')
->setTitle(static::generateString(50))
->setDescription(static::generateString(500))
,
];
}
)
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('daily', (string) $result->getBody());
static::assertSame(
'Monthly - May, 2020 - Shaarli',
$assignedVariables['pagetitle']
);
static::assertCount(2, $assignedVariables['linksToDisplay']);
static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
static::assertSame('', $assignedVariables['previousday']);
static::assertSame('', $assignedVariables['nextday']);
static::assertSame('May, 2020', $assignedVariables['dayDesc']);
static::assertSame('month', $assignedVariables['type']);
static::assertSame('Monthly', $assignedVariables['localizedType']);
}
/**
* Test simple display RSS with week parameter
*/
public function testSimpleRssWeekly(): void
{
$dates = [
new \DateTimeImmutable('2020-05-19'),
new \DateTimeImmutable('2020-05-13'),
];
$expectedDates = [
new \DateTimeImmutable('2020-05-24 23:59:59'),
new \DateTimeImmutable('2020-05-17 23:59:59'),
];
$this->container->environment['QUERY_STRING'] = 'week';
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
return $key === 'week' ? '' : null;
});
$response = new Response();
$this->container->bookmarkService->expects(static::once())->method('search')->willReturn(
SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
(new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
(new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
])
);
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
static::assertSame('http://shaarli/subfolder/daily-rss?week', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(2, $assignedVariables['days']);
$day = $assignedVariables['days'][$dates[0]->format('YW')];
$date = $expectedDates[0];
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame('Week 21 (May 18, 2020)', $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?week=' . $dates[0]->format('YW'), $day['absolute_url']);
static::assertCount(1, $day['links']);
$day = $assignedVariables['days'][$dates[1]->format('YW')];
$date = $expectedDates[1];
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame('Week 20 (May 11, 2020)', $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?week=' . $dates[1]->format('YW'), $day['absolute_url']);
static::assertCount(2, $day['links']);
}
/**
* Test simple display RSS with month parameter
*/
public function testSimpleRssMonthly(): void
{
$dates = [
new \DateTimeImmutable('2022-02-19'),
new \DateTimeImmutable('2020-04-13'),
];
$expectedDates = [
new \DateTimeImmutable('2022-02-28 23:59:59'),
new \DateTimeImmutable('2020-04-30 23:59:59'),
];
$this->container->environment['QUERY_STRING'] = 'month';
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
return $key === 'month' ? '' : null;
});
$response = new Response();
$this->container->bookmarkService->expects(static::once())->method('search')->willReturn(
SearchResult::getSearchResult([
(new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
(new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
(new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
])
);
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
static::assertSame('http://shaarli/subfolder/daily-rss?month', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(2, $assignedVariables['days']);
$day = $assignedVariables['days'][$dates[0]->format('Ym')];
$date = $expectedDates[0];
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame('February, 2022', $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?month=' . $dates[0]->format('Ym'), $day['absolute_url']);
static::assertCount(1, $day['links']);
$day = $assignedVariables['days'][$dates[1]->format('Ym')];
$date = $expectedDates[1];
static::assertEquals($date, $day['date']);
static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
static::assertSame('April, 2020', $day['date_human']);
static::assertSame('http://shaarli/subfolder/daily?month=' . $dates[1]->format('Ym'), $day['absolute_url']);
static::assertCount(2, $day['links']);
}
}

View file

@ -1,98 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Front\Exception\ShaarliFrontException;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class ErrorControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var ErrorController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ErrorController($this->container);
}
/**
* Test displaying error with a ShaarliFrontException: display exception message and use its code for HTTTP code
*/
public function testDisplayFrontExceptionError(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$message = 'error message';
$errorCode = 418;
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = ($this->controller)(
$request,
$response,
new class ($message, $errorCode) extends ShaarliFrontException {
}
);
static::assertSame($errorCode, $result->getStatusCode());
static::assertSame($message, $assignedVariables['message']);
static::assertArrayNotHasKey('stacktrace', $assignedVariables);
}
/**
* Test displaying error with any exception (no debug) while logged in:
* display full error details
*/
public function testDisplayAnyExceptionErrorNoDebugLoggedIn(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$result = ($this->controller)($request, $response, new \Exception('abc'));
static::assertSame(500, $result->getStatusCode());
static::assertSame('Error: abc', $assignedVariables['message']);
static::assertContainsPolyfill('Please report it on Github', $assignedVariables['text']);
static::assertArrayHasKey('stacktrace', $assignedVariables);
}
/**
* Test displaying error with any exception (no debug) while logged out:
* display standard error without detail
*/
public function testDisplayAnyExceptionErrorNoDebug(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->loginManager->method('isLoggedIn')->willReturn(false);
$result = ($this->controller)($request, $response, new \Exception('abc'));
static::assertSame(500, $result->getStatusCode());
static::assertSame('An unexpected error occurred.', $assignedVariables['message']);
static::assertArrayNotHasKey('text', $assignedVariables);
static::assertArrayNotHasKey('stacktrace', $assignedVariables);
}
}

View file

@ -1,81 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Uri;
class ErrorNotFoundControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var ErrorNotFoundController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ErrorNotFoundController($this->container);
}
/**
* Test displaying 404 error
*/
public function testDisplayNotFoundError(): void
{
$request = $this->createMock(Request::class);
$request->expects(static::once())->method('getRequestTarget')->willReturn('/');
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = ($this->controller)(
$request,
$response
);
static::assertSame(404, $result->getStatusCode());
static::assertSame('404', (string) $result->getBody());
static::assertSame('Requested page could not be found.', $assignedVariables['error_message']);
}
/**
* Test displaying 404 error from REST API
*/
public function testDisplayNotFoundErrorFromAPI(): void
{
$request = $this->createMock(Request::class);
$request->expects(static::once())->method('getRequestTarget')->willReturn('/sufolder/api/v1/links');
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$result = ($this->controller)($request, $response);
static::assertSame(404, $result->getStatusCode());
static::assertSame([], $assignedVariables);
}
}

View file

@ -1,151 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Feed\FeedBuilder;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class FeedControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var FeedController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->feedBuilder = $this->createMock(FeedBuilder::class);
$this->controller = new FeedController($this->container);
}
/**
* Feed Controller - RSS default behaviour
*/
public function testDefaultRssController(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->feedBuilder->expects(static::once())->method('setLocale');
$this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
$this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_feed'])
->willReturnCallback(function (string $hook, array $data, array $param): void {
if ('render_feed' === $hook) {
static::assertSame('data', $data['content']);
static::assertArrayHasKey('loggedin', $param);
static::assertSame('feed.rss', $param['target']);
}
})
;
$result = $this->controller->rss($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('feed.rss', (string) $result->getBody());
static::assertSame('data', $assignedVariables['content']);
}
/**
* Feed Controller - ATOM default behaviour
*/
public function testDefaultAtomController(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->feedBuilder->expects(static::once())->method('setLocale');
$this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
$this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_feed'])
->willReturnCallback(function (string $hook, array $data, array $param): void {
if ('render_feed' === $hook) {
static::assertSame('data', $data['content']);
static::assertArrayHasKey('loggedin', $param);
static::assertSame('feed.atom', $param['target']);
}
})
;
$result = $this->controller->atom($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
static::assertSame('feed.atom', (string) $result->getBody());
static::assertSame('data', $assignedVariables['content']);
}
/**
* Feed Controller - ATOM with parameters
*/
public function testAtomControllerWithParameters(): void
{
$request = $this->createMock(Request::class);
$request->method('getParams')->willReturn(['parameter' => 'value']);
$response = new Response();
// Save RainTPL assigned variables
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$this->container->feedBuilder
->method('buildData')
->with('atom', ['parameter' => 'value'])
->willReturn(['content' => 'data'])
;
// Make sure that PluginManager hook is triggered
$this->container->pluginManager
->expects(static::atLeastOnce())
->method('executeHooks')
->withConsecutive(['render_feed'])
->willReturnCallback(function (string $hook, array $data, array $param): void {
if ('render_feed' === $hook) {
static::assertSame('data', $data['content']);
static::assertArrayHasKey('loggedin', $param);
static::assertSame('feed.atom', $param['target']);
}
})
;
$result = $this->controller->atom($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
static::assertSame('feed.atom', (string) $result->getBody());
static::assertSame('data', $assignedVariables['content']);
}
}

View file

@ -1,122 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ShaarliTestContainer;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\BookmarkRawFormatter;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
/**
* Trait FrontControllerMockHelper
*
* Helper trait used to initialize the ShaarliContainer and mock its services for controller tests.
*
* @property ShaarliTestContainer $container
* @package Shaarli\Front\Controller
*/
trait FrontControllerMockHelper
{
/** @var ShaarliTestContainer */
protected $container;
/**
* Mock the container instance and initialize container's services used by tests
*/
protected function createContainer(): void
{
$this->container = $this->createMock(ShaarliTestContainer::class);
$this->container->loginManager = $this->createMock(LoginManager::class);
// Config
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
if ($parameter === 'general.tags_separator') {
return '@';
}
return $default === null ? $parameter : $default;
});
// PageBuilder
$this->container->pageBuilder = $this->createMock(PageBuilder::class);
$this->container->pageBuilder
->method('render')
->willReturnCallback(function (string $template): string {
return $template;
})
;
// Plugin Manager
$this->container->pluginManager = $this->createMock(PluginManager::class);
// BookmarkService
$this->container->bookmarkService = $this->createMock(BookmarkServiceInterface::class);
// Formatter
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
$this->container->formatterFactory
->method('getFormatter')
->willReturnCallback(function (): BookmarkFormatter {
return new BookmarkRawFormatter($this->container->conf, true);
})
;
// CacheManager
$this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
// SessionManager
$this->container->sessionManager = $this->createMock(SessionManager::class);
// $_SERVER
$this->container->environment = [
'SERVER_NAME' => 'shaarli',
'SERVER_PORT' => '80',
'REQUEST_URI' => '/subfolder/daily-rss',
'REMOTE_ADDR' => '1.2.3.4',
'SCRIPT_NAME' => '/subfolder/index.php',
];
$this->container->basePath = '/subfolder';
}
/**
* Pass a reference of an array which will be populated by `pageBuilder->assign` calls during execution.
*
* @param mixed $variables Array reference to populate.
*/
protected function assignTemplateVars(array &$variables): void
{
$this->container->pageBuilder
->method('assign')
->willReturnCallback(function ($key, $value) use (&$variables) {
$variables[$key] = $value;
return $this;
})
;
}
protected static function generateString(int $length): string
{
// bin2hex(random_bytes) generates string twice as long as given parameter
$length = (int) ceil($length / 2);
return bin2hex(random_bytes($length));
}
/**
* Force to be used in PHPUnit context.
*/
abstract protected function isInTestsContext(): bool;
}

View file

@ -1,304 +0,0 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\AlreadyInstalledException;
use Shaarli\Security\SessionManager;
use Shaarli\TestCase;
use Slim\Http\Request;
use Slim\Http\Response;
class InstallControllerTest extends TestCase
{
use FrontControllerMockHelper;
protected const MOCK_FILE = '.tmp';
/** @var InstallController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('getConfigFileExt')->willReturn(static::MOCK_FILE);
$this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
if ($key === 'resource.raintpl_tpl') {
return '.';
}
return $default ?? $key;
});
$this->controller = new InstallController($this->container);
}
protected function tearDown(): void
{
if (file_exists(static::MOCK_FILE)) {
unlink(static::MOCK_FILE);
}
}
/**
* Test displaying install page with valid session.
*/
public function testInstallIndexWithValidSession(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $key, $default) {
return $key === 'session_tested' ? 'Working' : $default;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('install', (string) $result->getBody());
static::assertIsArray($assignedVariables['continents']);
static::assertSame('Africa', $assignedVariables['continents'][0]);
static::assertSame('UTC', $assignedVariables['continents']['selected']);
static::assertIsArray($assignedVariables['cities']);
static::assertSame(['continent' => 'Africa', 'city' => 'Abidjan'], $assignedVariables['cities'][0]);
static::assertSame('UTC', $assignedVariables['continents']['selected']);
static::assertIsArray($assignedVariables['languages']);
static::assertSame('Automatic', $assignedVariables['languages']['auto']);
static::assertSame('French', $assignedVariables['languages']['fr']);
static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
static::assertArrayHasKey('php_eol', $assignedVariables);
static::assertArrayHasKey('php_extensions', $assignedVariables);
static::assertArrayHasKey('permissions', $assignedVariables);
static::assertEmpty($assignedVariables['permissions']);
static::assertSame('Install Shaarli', $assignedVariables['pagetitle']);
}
/**
* Instantiate the install controller with an existing config file: exception.
*/
public function testInstallWithExistingConfigFile(): void
{
$this->expectException(AlreadyInstalledException::class);
touch(static::MOCK_FILE);
$this->controller = new InstallController($this->container);
}
/**
* Call controller without session yet defined, redirect to test session install page.
*/
public function testInstallRedirectToSessionTest(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(InstallController::SESSION_TEST_KEY, InstallController::SESSION_TEST_VALUE)
;
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/install/session-test', $result->getHeader('location')[0]);
}
/**
* Call controller in session test mode: valid session then redirect to install page.
*/
public function testInstallSessionTestValid(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->method('getSessionParameter')
->with(InstallController::SESSION_TEST_KEY)
->willReturn(InstallController::SESSION_TEST_VALUE)
;
$result = $this->controller->sessionTest($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/install', $result->getHeader('location')[0]);
}
/**
* Call controller in session test mode: invalid session then redirect to error page.
*/
public function testInstallSessionTestError(): void
{
$assignedVars = [];
$this->assignTemplateVars($assignedVars);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->method('getSessionParameter')
->with(InstallController::SESSION_TEST_KEY)
->willReturn('KO')
;
$result = $this->controller->sessionTest($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('error', (string) $result->getBody());
static::assertStringStartsWith(
'<pre>Sessions do not seem to work correctly on your server',
$assignedVars['message']
);
}
/**
* Test saving valid data from install form. Also initialize datastore.
*/
public function testSaveInstallValid(): void
{
$providedParameters = [
'continent' => 'Europe',
'city' => 'Berlin',
'setlogin' => 'bob',
'setpassword' => 'password',
'title' => 'Shaarli',
'language' => 'fr',
'updateCheck' => true,
'enableApi' => true,
];
$expectedSettings = [
'general.timezone' => 'Europe/Berlin',
'credentials.login' => 'bob',
'credentials.salt' => '_NOT_EMPTY',
'credentials.hash' => '_NOT_EMPTY',
'general.title' => 'Shaarli',
'translation.language' => 'en',
'updates.check_updates' => true,
'api.enabled' => true,
'api.secret' => '_NOT_EMPTY',
'general.header_link' => '/subfolder',
];
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) use ($providedParameters) {
return $providedParameters[$key] ?? null;
});
$response = new Response();
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $value) {
if ($key === 'credentials.login') {
return 'bob';
} elseif ($key === 'credentials.salt') {
return 'salt';
}
return $value;
})
;
$this->container->conf
->expects(static::exactly(count($expectedSettings)))
->method('set')
->willReturnCallback(function (string $key, $value) use ($expectedSettings) {
if ($expectedSettings[$key] ?? null === '_NOT_EMPTY') {
static::assertNotEmpty($value);
} else {
static::assertSame($expectedSettings[$key], $value);
}
})
;
$this->container->conf->expects(static::once())->method('write');
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->with(SessionManager::KEY_SUCCESS_MESSAGES)
;
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
}
/**
* Test default settings (timezone and title).
* Also check that bookmarks are not initialized if
*/
public function testSaveInstallDefaultValues(): void
{
$confSettings = [];
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
$confSettings[$key] = $value;
});
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
static::assertSame('UTC', $confSettings['general.timezone']);
static::assertSame('Shared Bookmarks', $confSettings['general.title']);
}
/**
* Same test as testSaveInstallDefaultValues() but for an instance install in root directory.
*/
public function testSaveInstallDefaultValuesWithoutSubfolder(): void
{
$confSettings = [];
$this->container->environment = [
'SERVER_NAME' => 'shaarli',
'SERVER_PORT' => '80',
'REQUEST_URI' => '/install',
'REMOTE_ADDR' => '1.2.3.4',
'SCRIPT_NAME' => '/index.php',
];
$this->container->basePath = '';
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
$confSettings[$key] = $value;
});
$result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/login', $result->getHeader('location')[0]);
static::assertSame('UTC', $confSettings['general.timezone']);
static::assertSame('Shared Bookmarks', $confSettings['general.title']);
}
}

Some files were not shown because too many files have changed in this diff Show more