14 Commits

Author SHA1 Message Date
arian 93010caee4 Merge branch 'e2e-testing'
Build and Test Debian Package / build (push) Successful in 28s
Build and Test Debian Package / test (push) Successful in 14s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-06-05 05:47:33 -04:00
arian ded42ccd03 chore(deps): bump python dependencies
Build and Test Debian Package / build (push) Successful in 33s
Build and Test Debian Package / test (push) Successful in 52s
2026-06-04 19:36:30 -04:00
arian 9255f9b052 refactor(tests): remove a test
Build and Test Debian Package / build (push) Successful in 28s
Build and Test Debian Package / test (push) Successful in 30s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-06-01 03:37:18 -04:00
arian 6e22dcdda5 fix(tests): change edge case file extension
Build and Test Debian Package / build (push) Successful in 27s
Build and Test Debian Package / test (push) Failing after 32s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-06-01 03:34:41 -04:00
arian ef1e42bcbb feat(tests): additional tests for uploads
Build and Test Debian Package / build (push) Successful in 28s
Build and Test Debian Package / test (push) Failing after 31s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-06-01 03:31:26 -04:00
arian 0912c338e3 chore(debian): v0.3.0 changelog added
Build and Test Debian Package / build (push) Successful in 7m11s
Build and Test Debian Package / test (push) Successful in 3m50s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 09:03:35 -04:00
arian 0aff62d050 Merge branch 'e2e-testing'
Build and Test Debian Package / build (push) Successful in 7s
Build and Test Debian Package / test (push) Successful in 4s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 08:21:47 -04:00
arian 15bcd258af feat(ci): update workflow to include testing after deb package build
Build and Test Debian Package / build (push) Successful in 7s
Build and Test Debian Package / test (push) Successful in 55s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 08:16:09 -04:00
arian 7599093d90 feat(e2e): add Dockerfile and entrypoint script for testing deb packages
Build Debian Package / build (push) Successful in 7s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 08:06:48 -04:00
arian b495a67bb8 e2e(api): add initial functional tests & pytest configuration
Build Debian Package / build (push) Successful in 8s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 08:04:25 -04:00
arian df8b20edfb Merge branch 'pr-arch'
Build Debian Package / build (push) Successful in 7s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 02:23:16 -04:00
arian 671e38429c fix(debian): change architecture from 'all' to 'amd64'
Build Debian Package / build (push) Successful in 7s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 02:19:49 -04:00
arian ce8165271d ci(build): dynamically retrieve Debian package filename
Build Debian Package / build (push) Successful in 6s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-31 02:17:40 -04:00
arian 56570e7b00 Merge branch 'dropzone-upstream'
Build Debian Package / build (push) Successful in 6s
Signed-off-by: Arian Nasr <arian@2ari.ca>
2026-05-28 21:54:25 -04:00
9 changed files with 197 additions and 6 deletions
+35 -4
View File
@@ -1,4 +1,4 @@
name: Build Debian Package name: Build and Test Debian Package
on: on:
push: push:
@@ -10,7 +10,9 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
deb_filename: ${{ steps.get_deb_name.outputs.filename }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -23,9 +25,38 @@ jobs:
- name: Build Debian Package - name: Build Debian Package
run: ./release/build-docker.sh run: ./release/build-docker.sh
- name: Get Deb Filename
id: get_deb_name
run: |
DEB_FILENAME=$(ls output/*.deb | xargs basename)
echo "filename=$DEB_FILENAME" >> $GITHUB_OUTPUT
- name: Upload Build Artifact - name: Upload Build Artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: navidrome-uploader-deb name: ${{ steps.get_deb_name.outputs.filename }}
path: output/*.deb path: output/${{ steps.get_deb_name.outputs.filename }}
retention-days: 5 retention-days: 5
test:
runs-on: ubuntu-latest
needs: build # only run tests if the build job succeeded
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create output directory
run: mkdir -p output
- name: Download Build Artifact
uses: actions/download-artifact@v3
with:
name: ${{ needs.build.outputs.deb_filename }}
path: output/
- name: Build Test Docker Image
run: docker build -t uploader-tester -f Dockerfile.test .
- name: Run E2E Test Suite
run: docker run --rm uploader-tester
+36
View File
@@ -0,0 +1,36 @@
# Navidome-Uploader Dockerfile for testing .deb packages
# Arian Nasr
# May 31, 2026
FROM debian:stable
# Prevent interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
curl \
python3 \
python3-venv \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Copy the built deb package into the container
COPY output/*.deb /tmp/navidrome-uploader.deb
RUN apt-get update && \
apt-get install -y -f /tmp/navidrome-uploader.deb && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/navidrome/music && \
chown -R navidrome-uploader:navidrome-uploader /opt/navidrome/music
COPY e2e/ /e2e/
RUN python3 -m venv /e2e/venv && \
/e2e/venv/bin/pip install --no-cache-dir -r /e2e/requirements.txt
RUN chmod +x /e2e/test-entrypoint.sh
WORKDIR /e2e
ENTRYPOINT ["/e2e/test-entrypoint.sh"]
+17
View File
@@ -1,3 +1,20 @@
navidrome-uploader (0.3.0) unstable; urgency=medium
* E2E Testing:
- Add Dockerfile and entrypoint script for testing .deb packages
- Implement initial API functional tests with pytest configuration
- Update CI workflow to run E2E tests automatically after package build
* Packaging:
- Change architecture from 'all' to 'amd64'
* CI/Build:
- Implement automated Gitea workflow for .deb building
* Refactoring:
- Remove unused templates
* Maintenance:
- Update Dropzone.js upstream source
-- Arian Nasr <arian@2ari.ca> Sun, 31 May 2026 08:43:00 -0400
navidrome-uploader (0.2.0) unstable; urgency=medium navidrome-uploader (0.2.0) unstable; urgency=medium
* Packaging: * Packaging:
+1 -1
View File
@@ -7,6 +7,6 @@ Standards-Version: 4.7.0
Rules-Requires-Root: no Rules-Requires-Root: no
Package: navidrome-uploader Package: navidrome-uploader
Architecture: all Architecture: amd64
Depends: ${misc:Depends}, adduser, python3, python3-venv, python3-pip, python3-wheel Depends: ${misc:Depends}, adduser, python3, python3-venv, python3-pip, python3-wheel
Description: Navidrome Web Upload Utility Description: Navidrome Web Upload Utility
+3
View File
@@ -0,0 +1,3 @@
Werkzeug==3.1.8
pytest==9.0.3
requests==2.34.2
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
export NAVIDROME_MUSIC_FOLDER="/opt/navidrome/music"
export BASE_URL="http://127.0.0.1:5001"
# Start the app manually since systemd isn't running in the container
cd /opt/navidrome-uploader
su -s /bin/sh navidrome-uploader -c "/opt/navidrome-uploader/venv/bin/gunicorn --no-control-socket -c gunicorn.conf.py main:app" &
APP_PID=$!
TIMEOUT=30
SERVICE_UP=0
while [ $TIMEOUT -gt 0 ]; do
if curl -s $BASE_URL/ping | grep -q "pong"; then
SERVICE_UP=1
break
fi
sleep 1
TIMEOUT=$((TIMEOUT-1))
done
if [ $SERVICE_UP -eq 0 ]; then
echo "Error: Service failed to start within the timeout period"
kill $APP_PID
exit 1
fi
cd /e2e
/e2e/venv/bin/pytest unit/api/test_api.py
# Capture the exit code of pytest
TEST_EXIT_CODE=$?
kill $APP_PID
exit $TEST_EXIT_CODE
+55
View File
@@ -0,0 +1,55 @@
import pytest
import requests
import io
import os
from werkzeug.utils import secure_filename
def test_api_ping(base_url):
response = requests.get(f'{base_url}/ping')
assert response.status_code == 200
assert response.text == 'pong'
def test_api_upload_non_audio_file(base_url, upload_folder):
files = {'file': ('test.txt', io.BytesIO(b'not an audio file'))}
expected_filename = os.path.join(upload_folder, secure_filename('test.txt'))
response = requests.post(f'{base_url}/', files=files)
assert response.status_code == 400
assert "not allowed" in response.json().get("error", "")
assert not os.path.exists(expected_filename)
if os.path.exists(expected_filename):
os.remove(expected_filename)
def test_api_upload_mp3_file(base_url, upload_folder):
files = {'file': ('test.mp3', io.BytesIO(b'fake mp3 content'))}
expected_filename = os.path.join(upload_folder, secure_filename('test.mp3'))
response = requests.post(f'{base_url}/', files=files)
assert response.status_code == 200
assert "uploaded successfully" in response.json().get("message", "")
assert os.path.exists(expected_filename)
if os.path.exists(expected_filename):
os.remove(expected_filename)
def test_api_upload_flac_file(base_url, upload_folder):
files = {'file': ('test.flac', io.BytesIO(b'fake flac content'))}
expected_filename = os.path.join(upload_folder, secure_filename('test.flac'))
response = requests.post(f'{base_url}/', files=files)
assert response.status_code == 200
assert "uploaded successfully" in response.json().get("message", "")
assert os.path.exists(expected_filename)
if os.path.exists(expected_filename):
os.remove(expected_filename)
def test_api_upload_m4a_file(base_url, upload_folder):
files = {'file': ('test.m4a', io.BytesIO(b'fake m4a content'))}
expected_filename = os.path.join(upload_folder, secure_filename('test.m4a'))
response = requests.post(f'{base_url}/', files=files)
assert response.status_code == 400
assert "not allowed" in response.json().get("error", "")
assert not os.path.exists(expected_filename)
if os.path.exists(expected_filename):
os.remove(expected_filename)
+10
View File
@@ -0,0 +1,10 @@
import pytest
import os
@pytest.fixture(scope='session')
def base_url():
return os.getenv('BASE_URL', 'http://127.0.0.1:5001')
@pytest.fixture(scope='session')
def upload_folder():
return os.getenv('NAVIDROME_MUSIC_FOLDER', '/opt/navidrome/music')
+1 -1
View File
@@ -6,5 +6,5 @@ Jinja2==3.1.6
MarkupSafe==3.0.3 MarkupSafe==3.0.3
Werkzeug==3.1.8 Werkzeug==3.1.8
gunicorn==26.0.0 gunicorn==26.0.0
pip==26.1.1 pip==26.1.2
packaging==26.2 packaging==26.2