Compare commits
2 Commits
92b82da3af
...
87a1a2d89a
| Author | SHA1 | Date | |
|---|---|---|---|
| 87a1a2d89a | |||
| 1ada52f5f8 |
@@ -0,0 +1,15 @@
|
|||||||
|
# Test Database Configuration
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_USERNAME=testuser
|
||||||
|
DB_PASSWORD=testpass
|
||||||
|
DB_NAME=music_server_test
|
||||||
|
|
||||||
|
# Test Paths
|
||||||
|
MUSIC_PATH=./testMusic
|
||||||
|
CHARACTERS_PATH=./testCharacters
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
PORT=8081
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
LOG_JSON=false
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
name: Integration Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch: # Manual trigger only
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
integration-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: golang:1.25
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: testuser
|
||||||
|
POSTGRES_PASSWORD: testpass
|
||||||
|
POSTGRES_DB: music_server_test
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Install testcontainers
|
||||||
|
run: go install github.com/testcontainers/testcontainers-go@latest
|
||||||
|
|
||||||
|
- name: Run integration tests
|
||||||
|
env:
|
||||||
|
DB_HOST: postgres
|
||||||
|
DB_PORT: 5432
|
||||||
|
DB_USERNAME: testuser
|
||||||
|
DB_PASSWORD: testpass
|
||||||
|
DB_NAME: music_server_test
|
||||||
|
MUSIC_PATH: ./testMusic
|
||||||
|
CHARACTERS_PATH: ./testCharacters
|
||||||
|
run: go test -v -timeout 30m ./...
|
||||||
@@ -11,13 +11,35 @@ require (
|
|||||||
github.com/lib/pq v1.12.3
|
github.com/lib/pq v1.12.3
|
||||||
github.com/panjf2000/ants/v2 v2.12.0
|
github.com/panjf2000/ants/v2 v2.12.0
|
||||||
github.com/spf13/afero v1.15.0
|
github.com/spf13/afero v1.15.0
|
||||||
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/swaggo/echo-swagger/v2 v2.0.1
|
github.com/swaggo/echo-swagger/v2 v2.0.1
|
||||||
github.com/swaggo/swag v1.16.6
|
github.com/swaggo/swag v1.16.6
|
||||||
|
github.com/testcontainers/testcontainers-go v0.42.0
|
||||||
|
go.uber.org/zap v1.28.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/containerd/platforms v0.2.1 // indirect
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/docker/go-connections v0.6.0 // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.10.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-errors/errors v1.5.1 // indirect
|
github.com/go-errors/errors v1.5.1 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||||
github.com/go-openapi/spec v0.22.4 // indirect
|
github.com/go-openapi/spec v0.22.4 // indirect
|
||||||
@@ -28,21 +50,46 @@ require (
|
|||||||
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
|
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
|
||||||
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
|
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
|
||||||
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
|
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/klauspost/compress v1.18.5 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.10 // indirect
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/moby/go-archive v0.2.0 // indirect
|
||||||
|
github.com/moby/moby/api v1.54.1 // indirect
|
||||||
|
github.com/moby/moby/client v0.4.0 // indirect
|
||||||
|
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||||
|
github.com/moby/sys/user v0.4.0 // indirect
|
||||||
|
github.com/moby/sys/userns v0.1.0 // indirect
|
||||||
|
github.com/moby/term v0.5.2 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
|
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||||
github.com/sv-tools/openapi v0.4.0 // indirect
|
github.com/sv-tools/openapi v0.4.0 // indirect
|
||||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||||
github.com/swaggo/swag/v2 v2.0.0-rc5 // indirect
|
github.com/swaggo/swag/v2 v2.0.0-rc5 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.41.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.41.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.41.0 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.uber.org/zap v1.28.0 // indirect
|
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/mod v0.36.0 // indirect
|
golang.org/x/mod v0.36.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
|
golang.org/x/sys v0.44.0 // indirect
|
||||||
golang.org/x/text v0.37.0 // indirect
|
golang.org/x/text v0.37.0 // indirect
|
||||||
golang.org/x/time v0.15.0 // indirect
|
golang.org/x/time v0.15.0 // indirect
|
||||||
golang.org/x/tools v0.45.0 // indirect
|
golang.org/x/tools v0.45.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
github.com/MShekow/directory-checksum v1.4.18 h1:1nPPVl7uREa6WMTAPKoWW/GylhnASs0C9C+GPiwLwXA=
|
github.com/MShekow/directory-checksum v1.4.18 h1:1nPPVl7uREa6WMTAPKoWW/GylhnASs0C9C+GPiwLwXA=
|
||||||
@@ -8,11 +12,22 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
|||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
||||||
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||||
|
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||||
|
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
|
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -22,18 +37,23 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
|||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||||
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
|
||||||
|
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
||||||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||||
@@ -65,8 +85,11 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
|
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
|
||||||
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
|
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
@@ -75,24 +98,44 @@ github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
|||||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo/v5 v5.1.1 h1:4QkvKoS8ps5ch49t8b72QS9Z581ytgxhTzxuB/CBA2I=
|
github.com/labstack/echo/v5 v5.1.1 h1:4QkvKoS8ps5ch49t8b72QS9Z581ytgxhTzxuB/CBA2I=
|
||||||
github.com/labstack/echo/v5 v5.1.1/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
|
github.com/labstack/echo/v5 v5.1.1/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
|
||||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
|
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||||
|
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||||
|
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
|
||||||
|
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||||
|
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
|
||||||
|
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
|
||||||
|
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
|
||||||
|
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
|
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||||
|
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||||
|
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||||
|
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||||
|
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||||
|
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||||
|
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||||
|
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
github.com/panjf2000/ants/v2 v2.12.0 h1:u9JhESo83i/GkZnhfTNuFMMWcNt7mnV1bGJ6FT4wXH8=
|
github.com/panjf2000/ants/v2 v2.12.0 h1:u9JhESo83i/GkZnhfTNuFMMWcNt7mnV1bGJ6FT4wXH8=
|
||||||
github.com/panjf2000/ants/v2 v2.12.0/go.mod h1:tSQuaNQ6r6NRhPt+IZVUevvDyFMTs+eS4ztZc52uJTY=
|
github.com/panjf2000/ants/v2 v2.12.0/go.mod h1:tSQuaNQ6r6NRhPt+IZVUevvDyFMTs+eS4ztZc52uJTY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -100,11 +143,19 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||||
|
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
@@ -119,16 +170,30 @@ github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
|||||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||||
github.com/swaggo/swag/v2 v2.0.0-rc5 h1:fK7d6ET9rrEsdB8IyuwXREWMcyQN3N7gawGFbbrjgHk=
|
github.com/swaggo/swag/v2 v2.0.0-rc5 h1:fK7d6ET9rrEsdB8IyuwXREWMcyQN3N7gawGFbbrjgHk=
|
||||||
github.com/swaggo/swag/v2 v2.0.0-rc5/go.mod h1:kCL8Fu4Zl8d5tB2Bgj96b8wRowwrwk175bZHXfuGVFI=
|
github.com/swaggo/swag/v2 v2.0.0-rc5/go.mod h1:kCL8Fu4Zl8d5tB2Bgj96b8wRowwrwk175bZHXfuGVFI=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||||
|
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||||
|
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
|
||||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
|
||||||
|
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
|
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
|
||||||
@@ -137,25 +202,37 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
|||||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||||
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||||
|
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
|
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||||
|
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||||
|
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testContainer testcontainers.Container
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Start PostgreSQL container
|
||||||
|
log.Println("Starting PostgreSQL test container...")
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: "postgres:15-alpine",
|
||||||
|
ExposedPorts: []string{"5432/tcp"},
|
||||||
|
Env: map[string]string{
|
||||||
|
"POSTGRES_USER": "testuser",
|
||||||
|
"POSTGRES_PASSWORD": "testpass",
|
||||||
|
"POSTGRES_DB": "music_server_test",
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForLog("database system is ready to accept connections"),
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to start container: %v", err)
|
||||||
|
}
|
||||||
|
testContainer = container
|
||||||
|
|
||||||
|
// Get container's host and port
|
||||||
|
host, err := container.Endpoint(ctx, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to get container endpoint: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("PostgreSQL container running at: %s", host)
|
||||||
|
|
||||||
|
// Set environment variables for all tests
|
||||||
|
os.Setenv("DB_HOST", host)
|
||||||
|
os.Setenv("DB_PORT", "5432")
|
||||||
|
os.Setenv("DB_USERNAME", "testuser")
|
||||||
|
os.Setenv("DB_PASSWORD", "testpass")
|
||||||
|
os.Setenv("DB_NAME", "music_server_test")
|
||||||
|
os.Setenv("MUSIC_PATH", "./testMusic")
|
||||||
|
os.Setenv("CHARACTERS_PATH", "./testCharacters")
|
||||||
|
os.Setenv("PORT", "8081")
|
||||||
|
os.Setenv("LOG_LEVEL", "debug")
|
||||||
|
os.Setenv("LOG_JSON", "false")
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
log.Println("Running integration tests...")
|
||||||
|
exitCode := m.Run()
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
log.Println("Stopping test container...")
|
||||||
|
if err := container.Terminate(ctx); err != nil {
|
||||||
|
log.Printf("Failed to terminate container: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseConnection verifies we can connect to the test database
|
||||||
|
func TestDatabaseConnection(t *testing.T) {
|
||||||
|
// This will be tested by the individual handler tests
|
||||||
|
// Just verify env vars are set
|
||||||
|
host := os.Getenv("DB_HOST")
|
||||||
|
port := os.Getenv("DB_PORT")
|
||||||
|
|
||||||
|
if host == "" || port == "" {
|
||||||
|
t.Error("Database environment variables not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Database configuration: host=%s, port=%s", host, port)
|
||||||
|
}
|
||||||
@@ -217,7 +217,7 @@ func syncGamesNew(full bool) {
|
|||||||
logging.GetLogger().Fatal("Failed to read music directory", zap.String("path", musicPath), zap.String("error", err.Error()))
|
logging.GetLogger().Fatal("Failed to read music directory", zap.String("path", musicPath), zap.String("error", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pool, _ := ants.NewPool(50, ants.WithPreAlloc(true))
|
pool, _ := ants.NewPool(10, ants.WithPreAlloc(true))
|
||||||
defer pool.Release()
|
defer pool.Release()
|
||||||
|
|
||||||
foldersSynced = 0
|
foldersSynced = 0
|
||||||
@@ -247,7 +247,7 @@ func checkBrokenSongsNew() {
|
|||||||
allSongs, err := repo.FetchAllSongs(db.Ctx)
|
allSongs, err := repo.FetchAllSongs(db.Ctx)
|
||||||
handleError("FetchAllSongs", err, "")
|
handleError("FetchAllSongs", err, "")
|
||||||
var brokenWg sync.WaitGroup
|
var brokenWg sync.WaitGroup
|
||||||
poolBroken, _ := ants.NewPool(50, ants.WithPreAlloc(true))
|
poolBroken, _ := ants.NewPool(200, ants.WithPreAlloc(true))
|
||||||
defer poolBroken.Release()
|
defer poolBroken.Release()
|
||||||
|
|
||||||
brokenWg.Add(len(allSongs))
|
brokenWg.Add(len(allSongs))
|
||||||
@@ -432,7 +432,7 @@ func newCheckSongs(entries []os.DirEntry, gameDir string, id int32) int32 {
|
|||||||
numberOfFiles := len(entries)
|
numberOfFiles := len(entries)
|
||||||
|
|
||||||
var songWg sync.WaitGroup
|
var songWg sync.WaitGroup
|
||||||
poolSong, _ := ants.NewPool(numberOfFiles, ants.WithPreAlloc(true))
|
poolSong, _ := ants.NewPool(10, ants.WithPreAlloc(true))
|
||||||
defer poolSong.Release()
|
defer poolSong.Release()
|
||||||
|
|
||||||
songWg.Add(numberOfFiles)
|
songWg.Add(numberOfFiles)
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSetupDB initializes the test database using existing functions
|
||||||
|
// It creates the database if it doesn't exist and runs migrations
|
||||||
|
func TestSetupDB(t *testing.T) {
|
||||||
|
host := os.Getenv("DB_HOST")
|
||||||
|
port := os.Getenv("DB_PORT")
|
||||||
|
user := os.Getenv("DB_USERNAME")
|
||||||
|
password := os.Getenv("DB_PASSWORD")
|
||||||
|
dbname := os.Getenv("DB_NAME")
|
||||||
|
|
||||||
|
if host == "" || port == "" || user == "" || password == "" || dbname == "" {
|
||||||
|
t.Skip("Test database environment variables not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use existing function to create database if it doesn't exist and run migrations
|
||||||
|
Migrate_db(host, port, user, password, dbname)
|
||||||
|
InitDB(host, port, user, password, dbname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTearDownDB closes the test database connection
|
||||||
|
func TestTearDownDB(t *testing.T) {
|
||||||
|
CloseDb()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestClearDatabase clears all data from the test database
|
||||||
|
// Useful for running tests with a clean slate
|
||||||
|
func TestClearDatabase(t *testing.T) {
|
||||||
|
if Dbpool == nil {
|
||||||
|
t.Skip("Database not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all tables in reverse order to respect foreign keys
|
||||||
|
// Note: This assumes the tables exist and have the expected structure
|
||||||
|
tables := []string{
|
||||||
|
"song_list",
|
||||||
|
"song",
|
||||||
|
"game",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
_, err := Dbpool.Exec(Ctx, "TRUNCATE TABLE "+table+" CASCADE")
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to truncate table %s: %v", table, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset sequences
|
||||||
|
_, err := Dbpool.Exec(Ctx, "SELECT setval('game_id_seq', 1, false)")
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to reset game_id_seq: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestCheckLatest verifies the /download endpoint
|
||||||
|
func TestCheckLatest(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping external API test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/download")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var version string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &version)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, version, "Should return version string")
|
||||||
|
t.Logf("Latest version: %s", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListAssetsOfLatest verifies the /download/list endpoint
|
||||||
|
func TestListAssetsOfLatest(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping external API test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/download/list")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var assets []string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &assets)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, assets, "Should return list of assets")
|
||||||
|
t.Logf("Found %d assets", len(assets))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDownloadLatestWindows verifies the /download/windows endpoint
|
||||||
|
func TestDownloadLatestWindows(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping external API test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/download/windows")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var url string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &url)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, url, "Should return download URL")
|
||||||
|
assert.Contains(t, url, "http", "URL should be valid")
|
||||||
|
t.Logf("Windows download URL: %s", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDownloadLatestLinux verifies the /download/linux endpoint
|
||||||
|
func TestDownloadLatestLinux(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping external API test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/download/linux")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var url string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &url)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, url, "Should return download URL")
|
||||||
|
assert.Contains(t, url, "http", "URL should be valid")
|
||||||
|
t.Logf("Linux download URL: %s", url)
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"music-server/internal/backend"
|
||||||
|
"music-server/internal/db"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestHealthCheck verifies the health endpoint returns database status
|
||||||
|
func TestHealthCheck(t *testing.T) {
|
||||||
|
// Setup database
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/health")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var healthData map[string]string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &healthData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, healthData)
|
||||||
|
assert.Equal(t, "up", healthData["status"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetVersion verifies the version endpoint returns version history
|
||||||
|
func TestGetVersion(t *testing.T) {
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/version")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var versionData backend.VersionData
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &versionData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, versionData.Version)
|
||||||
|
assert.NotEmpty(t, versionData.Changelog)
|
||||||
|
assert.NotEmpty(t, versionData.History)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetCharacterList verifies the characters endpoint returns list of characters
|
||||||
|
func TestGetCharacterList(t *testing.T) {
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/characters")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var characters []string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &characters)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, characters)
|
||||||
|
// Should contain our test characters
|
||||||
|
assert.Contains(t, characters, "char1.jpg")
|
||||||
|
assert.Contains(t, characters, "char2.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetCharacter verifies the character endpoint returns a file
|
||||||
|
func TestGetCharacter(t *testing.T) {
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/character?name=char1.jpg")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
// The response should be the file content
|
||||||
|
assert.NotEmpty(t, resp.Body.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetCharacterNotFound verifies handling of non-existent character
|
||||||
|
func TestGetCharacterNotFound(t *testing.T) {
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/character?name=nonexistent.jpg")
|
||||||
|
// Should return 404 or similar error
|
||||||
|
assert.NotEqual(t, http.StatusOK, resp.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDBTest verifies the database test endpoint
|
||||||
|
func TestDBTest(t *testing.T) {
|
||||||
|
// Setup database
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/dbtest")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
assert.Contains(t, resp.Body.String(), "TestedDB")
|
||||||
|
}
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"music-server/internal/backend"
|
||||||
|
"music-server/internal/db"
|
||||||
|
"music-server/internal/db/repository"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v5"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensureSyncRan ensures that sync has been run before testing music endpoints
|
||||||
|
func ensureSyncRan(t *testing.T, e *echo.Echo) {
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
games, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
if len(games) == 0 {
|
||||||
|
// Run sync
|
||||||
|
t.Log("No games found, running sync first...")
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Wait for sync to complete
|
||||||
|
maxAttempts := 60
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
var progress backend.ProgressResponse
|
||||||
|
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||||
|
|
||||||
|
if progress.Progress == "100" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == maxAttempts-1 {
|
||||||
|
t.Error("Sync did not complete within timeout")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetAllGames verifies the /music/all/order endpoint
|
||||||
|
func TestGetAllGames(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/all/order")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var games []string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &games)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, games, "Should have games after sync")
|
||||||
|
t.Logf("Found %d games", len(games))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetAllGamesRandom verifies the /music/all/random endpoint
|
||||||
|
func TestGetAllGamesRandom(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/all/random")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var games []string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &games)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, games, "Should have games after sync")
|
||||||
|
|
||||||
|
// Verify it's shuffled (not in original order)
|
||||||
|
// We can't easily verify randomness, but we can check it's the same length
|
||||||
|
resp2 := MakeTestRequest(t, e, "GET", "/music/all/order")
|
||||||
|
var gamesOrdered []string
|
||||||
|
json.Unmarshal(resp2.Body.Bytes(), &gamesOrdered)
|
||||||
|
assert.Equal(t, len(games), len(gamesOrdered), "Random and ordered should have same count")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetRandomSong verifies the /music/rand endpoint
|
||||||
|
func TestGetRandomSong(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songPath string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||||
|
assert.Contains(t, songPath, "testMusic/", "Path should be in testMusic directory")
|
||||||
|
t.Logf("Random song: %s", songPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetRandomSongLowChance verifies the /music/rand/low endpoint
|
||||||
|
func TestGetRandomSongLowChance(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/rand/low")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songPath string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetRandomSongClassic verifies the /music/rand/classic endpoint
|
||||||
|
func TestGetRandomSongClassic(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/rand/classic")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songPath string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetSongInfo verifies the /music/info endpoint
|
||||||
|
func TestGetSongInfo(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and get a song first
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/info")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var info backend.SongInfo
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &info)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, info.CurrentlyPlaying, "Should have a currently playing song")
|
||||||
|
assert.NotEmpty(t, info.Song, "Should have song name")
|
||||||
|
t.Logf("Song info: Game=%s, Song=%s", info.Game, info.Song)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetPlayedSongs verifies the /music/list endpoint
|
||||||
|
func TestGetPlayedSongs(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add some songs to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/list")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songs []backend.SongInfo
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songs, "Should have played songs in queue")
|
||||||
|
t.Logf("Found %d songs in queue", len(songs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetNextSong verifies the /music/next endpoint
|
||||||
|
func TestGetNextSong(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add songs to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/next")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songPath string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetPreviousSong verifies the /music/previous endpoint
|
||||||
|
func TestGetPreviousSong(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add songs to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
// Move forward
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/next")
|
||||||
|
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/previous")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var songPath string
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestResetMusic verifies the /music/reset endpoint
|
||||||
|
func TestResetMusic(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add songs to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
|
||||||
|
// Verify queue has items
|
||||||
|
respBefore := MakeTestRequest(t, e, "GET", "/music/list")
|
||||||
|
var songsBefore []backend.SongInfo
|
||||||
|
json.Unmarshal(respBefore.Body.Bytes(), &songsBefore)
|
||||||
|
assert.True(t, len(songsBefore) > 0, "Should have songs before reset")
|
||||||
|
|
||||||
|
// Reset queue
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/reset")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Verify queue is empty
|
||||||
|
respAfter := MakeTestRequest(t, e, "GET", "/music/list")
|
||||||
|
var songsAfter []backend.SongInfo
|
||||||
|
json.Unmarshal(respAfter.Body.Bytes(), &songsAfter)
|
||||||
|
assert.Equal(t, 0, len(songsAfter), "Queue should be empty after reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAddLatestToQue verifies the /music/addQue endpoint
|
||||||
|
func TestAddLatestToQue(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
|
||||||
|
// Get a random song (this sets lastFetchedNew)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
|
||||||
|
// Add to queue
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Verify it was added to queue
|
||||||
|
respList := MakeTestRequest(t, e, "GET", "/music/list")
|
||||||
|
var songs []backend.SongInfo
|
||||||
|
json.Unmarshal(respList.Body.Bytes(), &songs)
|
||||||
|
assert.True(t, len(songs) > 0, "Song should be in queue")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAddLatestPlayed verifies the /music/addPlayed endpoint
|
||||||
|
func TestAddLatestPlayed(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add song to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
|
||||||
|
// Mark as played
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/music/addPlayed")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPutPlayed verifies the PUT /music/played endpoint
|
||||||
|
func TestPutPlayed(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Ensure sync has run and add songs to queue
|
||||||
|
ensureSyncRan(t, e)
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||||
|
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||||
|
|
||||||
|
// Mark song 0 as played
|
||||||
|
resp := MakeTestRequestWithBody(t, e, "PUT", "/music/played?song=0", nil)
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"music-server/internal/backend"
|
||||||
|
"music-server/internal/db"
|
||||||
|
"music-server/internal/db/repository"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSyncPopulatesDatabase verifies that sync populates the database with games
|
||||||
|
func TestSyncPopulatesDatabase(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Before sync - should have no games (or very few if previous test ran)
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
gamesBefore, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
beforeCount := len(gamesBefore)
|
||||||
|
t.Logf("Games before sync: %d", beforeCount)
|
||||||
|
|
||||||
|
// Run sync
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Wait for sync to complete by polling /sync/progress
|
||||||
|
maxAttempts := 60
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
assert.Equal(t, http.StatusOK, progressResp.Code)
|
||||||
|
|
||||||
|
var progress backend.ProgressResponse
|
||||||
|
err := json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
t.Logf("Sync progress: %s%% (time spent: %s)", progress.Progress, progress.TimeSpent)
|
||||||
|
|
||||||
|
if progress.Progress == "100" {
|
||||||
|
t.Log("Sync completed!")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == maxAttempts-1 {
|
||||||
|
t.Error("Sync did not complete within timeout")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// After sync - should have games
|
||||||
|
gamesAfter, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
afterCount := len(gamesAfter)
|
||||||
|
t.Logf("Games after sync: %d", afterCount)
|
||||||
|
|
||||||
|
// Should have more games than before (unless database was already populated)
|
||||||
|
assert.True(t, afterCount > 0, "Database should have games after sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSyncMakesDifference verifies that sync actually changes the database state
|
||||||
|
func TestSyncMakesDifference(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Clear any existing data first
|
||||||
|
db.TestClearDatabase(t)
|
||||||
|
|
||||||
|
// Before sync - should have no games
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
gamesBefore, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(gamesBefore), "Should have no games before sync")
|
||||||
|
|
||||||
|
// Run sync
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Wait for sync to complete
|
||||||
|
maxAttempts := 60
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
var progress backend.ProgressResponse
|
||||||
|
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||||
|
|
||||||
|
if progress.Progress == "100" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == maxAttempts-1 {
|
||||||
|
t.Error("Sync did not complete within timeout")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// After sync - should have games
|
||||||
|
gamesAfter, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, len(gamesAfter) > 0, "Should have games after sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSyncProgress verifies the sync progress endpoint
|
||||||
|
func TestSyncProgress(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Start sync in background
|
||||||
|
go MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
|
||||||
|
// Poll progress endpoint
|
||||||
|
maxAttempts := 30
|
||||||
|
foundNonZero := false
|
||||||
|
foundComplete := false
|
||||||
|
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
var progress backend.ProgressResponse
|
||||||
|
err := json.Unmarshal(resp.Body.Bytes(), &progress)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
t.Logf("Sync progress: %s%%", progress.Progress)
|
||||||
|
|
||||||
|
// Verify we get valid progress values
|
||||||
|
if progress.Progress != "0" {
|
||||||
|
foundNonZero = true
|
||||||
|
}
|
||||||
|
if progress.Progress == "100" {
|
||||||
|
foundComplete = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, foundNonZero, "Should have seen non-zero progress")
|
||||||
|
assert.True(t, foundComplete, "Should have seen completion at 100%")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSyncGamesNewOnlyChanges verifies the incremental sync endpoint
|
||||||
|
func TestSyncGamesNewOnlyChanges(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Run full sync first
|
||||||
|
MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
// Wait for it to complete
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
// Get initial count
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
gamesBefore, _ := repo.FindAllGames(db.Ctx)
|
||||||
|
beforeCount := len(gamesBefore)
|
||||||
|
|
||||||
|
// Run incremental sync (should not change count if nothing changed)
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/new")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Wait a bit
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// Count should be the same
|
||||||
|
gamesAfter, _ := repo.FindAllGames(db.Ctx)
|
||||||
|
afterCount := len(gamesAfter)
|
||||||
|
|
||||||
|
// Note: This might not be exactly equal due to timing, but should be close
|
||||||
|
t.Logf("Games before incremental sync: %d, after: %d", beforeCount, afterCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestResetGames verifies the reset endpoint clears the database
|
||||||
|
// RUN THIS LAST
|
||||||
|
func TestResetGames(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// First ensure we have data
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
gamesBefore, _ := repo.FindAllGames(db.Ctx)
|
||||||
|
beforeCount := len(gamesBefore)
|
||||||
|
|
||||||
|
if beforeCount == 0 {
|
||||||
|
// Run sync to populate
|
||||||
|
MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
gamesBefore, _ = repo.FindAllGames(db.Ctx)
|
||||||
|
beforeCount = len(gamesBefore)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Games before reset: %d", beforeCount)
|
||||||
|
assert.True(t, beforeCount > 0, "Should have games to reset")
|
||||||
|
|
||||||
|
// Call reset
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/reset")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Verify database is cleared
|
||||||
|
// Note: reset might take a moment to propagate
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
gamesAfter, _ := repo.FindAllGames(db.Ctx)
|
||||||
|
afterCount := len(gamesAfter)
|
||||||
|
|
||||||
|
t.Logf("Games after reset: %d", afterCount)
|
||||||
|
assert.Equal(t, 0, afterCount, "Database should be empty after reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSyncGamesNewFull verifies the full sync endpoint
|
||||||
|
// RUN THIS LAST (before TestResetGames)
|
||||||
|
func TestSyncGamesNewFull(t *testing.T) {
|
||||||
|
db.TestSetupDB(t)
|
||||||
|
defer db.TestTearDownDB(t)
|
||||||
|
|
||||||
|
e := StartTestServer(t)
|
||||||
|
|
||||||
|
// Clear database first
|
||||||
|
db.TestClearDatabase(t)
|
||||||
|
|
||||||
|
// Run full sync
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
|
||||||
|
// Wait for sync to complete
|
||||||
|
maxAttempts := 60
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
var progress backend.ProgressResponse
|
||||||
|
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||||
|
|
||||||
|
if progress.Progress == "100" {
|
||||||
|
t.Log("Full sync completed")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == maxAttempts-1 {
|
||||||
|
t.Error("Full sync did not complete within timeout")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify database is populated
|
||||||
|
repo := repository.New(db.Dbpool)
|
||||||
|
games, err := repo.FindAllGames(db.Ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, len(games) > 0, "Database should be populated after full sync")
|
||||||
|
t.Logf("Full sync populated %d games", len(games))
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartTestServer starts the server for testing with test configuration
|
||||||
|
func StartTestServer(t *testing.T) *echo.Echo {
|
||||||
|
// Set test environment variables if not already set
|
||||||
|
if os.Getenv("DB_HOST") == "" {
|
||||||
|
os.Setenv("DB_HOST", "localhost")
|
||||||
|
}
|
||||||
|
if os.Getenv("DB_PORT") == "" {
|
||||||
|
os.Setenv("DB_PORT", "5432")
|
||||||
|
}
|
||||||
|
if os.Getenv("DB_USERNAME") == "" {
|
||||||
|
os.Setenv("DB_USERNAME", "testuser")
|
||||||
|
}
|
||||||
|
if os.Getenv("DB_PASSWORD") == "" {
|
||||||
|
os.Setenv("DB_PASSWORD", "testpass")
|
||||||
|
}
|
||||||
|
if os.Getenv("DB_NAME") == "" {
|
||||||
|
os.Setenv("DB_NAME", "music_server_test")
|
||||||
|
}
|
||||||
|
if os.Getenv("MUSIC_PATH") == "" {
|
||||||
|
os.Setenv("MUSIC_PATH", "./testMusic")
|
||||||
|
}
|
||||||
|
if os.Getenv("CHARACTERS_PATH") == "" {
|
||||||
|
os.Setenv("CHARACTERS_PATH", "./testCharacters")
|
||||||
|
}
|
||||||
|
if os.Getenv("PORT") == "" {
|
||||||
|
os.Setenv("PORT", "8081")
|
||||||
|
}
|
||||||
|
if os.Getenv("LOG_LEVEL") == "" {
|
||||||
|
os.Setenv("LOG_LEVEL", "debug")
|
||||||
|
}
|
||||||
|
if os.Getenv("LOG_JSON") == "" {
|
||||||
|
os.Setenv("LOG_JSON", "false")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a Server instance and get its routes
|
||||||
|
s := &Server{}
|
||||||
|
handler := s.RegisterRoutes()
|
||||||
|
|
||||||
|
// Wrap the http.Handler in an echo.Echo
|
||||||
|
e := echo.New()
|
||||||
|
// Use a custom handler that wraps our routes
|
||||||
|
e.Any("/*", echo.WrapHandler(handler))
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTestRequest makes an HTTP request to the test server
|
||||||
|
func MakeTestRequest(t *testing.T, e *echo.Echo, method, path string) *httptest.ResponseRecorder {
|
||||||
|
req := httptest.NewRequest(method, path, nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
e.ServeHTTP(rec, req)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTestRequestWithBody makes an HTTP request with a body to the test server
|
||||||
|
func MakeTestRequestWithBody(t *testing.T, e *echo.Echo, method, path string, body []byte) *httptest.ResponseRecorder {
|
||||||
|
req := httptest.NewRequest(method, path, nil)
|
||||||
|
if body != nil {
|
||||||
|
req = httptest.NewRequest(method, path, bytes.NewBuffer(body))
|
||||||
|
}
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
e.ServeHTTP(rec, req)
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForSyncComplete polls the sync progress endpoint until sync is complete
|
||||||
|
func WaitForSyncComplete(t *testing.T, e *echo.Echo, timeout time.Duration) bool {
|
||||||
|
start := time.Now()
|
||||||
|
for time.Since(start) < timeout {
|
||||||
|
resp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||||
|
if resp.Code != http.StatusOK {
|
||||||
|
t.Logf("Sync progress endpoint returned status %d", resp.Code)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse response - we can't easily decode here without importing backend
|
||||||
|
// Just check if response contains "100"
|
||||||
|
body := resp.Body.String()
|
||||||
|
if len(body) > 0 {
|
||||||
|
t.Logf("Sync progress: %s", body)
|
||||||
|
// Simple check for completion
|
||||||
|
// In a real scenario, you'd parse the JSON properly
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
t.Error("Sync did not complete within timeout")
|
||||||
|
return false
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 83 B |
Binary file not shown.
|
After Width: | Height: | Size: 67 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user