fix: fixes path issues on windows (#829)

* return corrent path in windows with usiing path lib

* ci: run tests in windows and macos too

* avoid testing mysql/psql in windows and macos

* windows & macos matrix

* disable gotestfmt

* replace which in shell script

* handle error in test db removal

* fix expected path baseed on platform

* add leading seprator

* proper temporary storage dir and db cleanup

* fix failed to create destination dir file does not exist in windows

* move temp to /tmp

* update temp folder

* fix config tests in windows

* apply patch for db

* revert temp dir creation.

* unify account db tests pach

* remove TmpDir for sqlite tests

* try to force CGO disable with enviroment variable

* Remove unneeded log

Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com>

* remove unneeded comment

Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com>

* fix file path for download images

* change way to create temp directory

* use diffrent file name for each test

* fix typo

* fix absolute path in successful download image

* correct filename with png

* change test to download image from internet instead of local machine

* remvoe unneeded import

* remove os.RemoveAll(.env)

* unify variable names in unit test

* return CGO_ENABLED=0

* test other way to set enviroment variable

* try to set enviroment variable sepratly in macos and windows

* set enviroment variable before run commands in windows

* fix windows test name

* combine two workflow for windows and macos again

* fix typo

* remove env

* change env path

* cleanup unneeded env

* general CGO_ENABLED environ

* use absolute path to run fileserver instead of relative

* serve file test from internet shiori repository

* check file existance after download and unify varibale name from temp to tmpDir

* remove unneeded log

---------

Co-authored-by: Felipe M <me@fmartingr.com>
Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com>
This commit is contained in:
Monirzadeh 2024-02-05 12:51:23 +03:30 committed by GitHub
parent 15b2a1e5f1
commit 82aa1e5080
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 241 additions and 449 deletions

View file

@ -2,8 +2,11 @@ name: "Unit Tests"
on: workflow_call
env:
CGO_ENABLED: 0
jobs:
test:
test-linux:
runs-on: ubuntu-latest
services:
postgres:
@ -27,7 +30,7 @@ jobs:
ports:
- 3306:3306
name: Go unit tests
name: Go unit tests (ubuntu-latest)
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@ -53,10 +56,41 @@ jobs:
env:
SHIORI_TEST_PG_URL: "postgres://shiori:shiori@localhost:5432/shiori?sslmode=disable"
SHIORI_TEST_MYSQL_URL: "shiori:shiori@(localhost:3306)/shiori"
CGO_ENABLED: 1 # go test -race requires cgo
- run: CGO_ENABLED=0 go build -tags osusergo,netgo -ldflags="-s -w -X main.version=$(git describe --tags) -X main.date=$(date --iso-8601=seconds)"
- run: go build -tags osusergo,netgo -ldflags="-s -w -X main.version=$(git describe --tags) -X main.date=$(date --iso-8601=seconds)"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # 3.1.5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test-windows-macos:
strategy:
matrix:
os: [windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
name: Go unit tests (${{ matrix.os }})
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: ./go.mod
- uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # 3.3.3
with:
path: |
~/.cache/go-build
~/go/pkg
key: golangci-lint.cache-{platform-arch}-{interval_number}-{go.mod_hash}
restore-keys: |
golangci-lint.cache-{interval_number}-
golangci-lint.cache-
- run: make unittest GO_TEST_FLAGS="-tags test_sqlite_only"
env:
CGO_ENABLED: 1 # go test -race requires cgo
- run: go build -tags osusergo,netgo -ldflags="-s -w -X main.version=$(git describe --tags) -X main.date=$(date --iso-8601=seconds)"

View file

@ -5,7 +5,7 @@ BASH ?= $(shell command -v bash 2> /dev/null)
SHIORI_DIR ?= dev-data
# Testing
GO_TEST_FLAGS ?= -v -race -count=1 -covermode=atomic -coverprofile=coverage.out
override GO_TEST_FLAGS += -v -race -count=1 -covermode=atomic -coverprofile=coverage.out
GOTESTFMT_FLAGS ?=
# Build

View file

@ -67,12 +67,7 @@ func TestReadDotEnv(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
os.Chdir(tmpDir)
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(tmpDir))
})
require.NoError(t, os.Chdir(tmpDir))
// Write the .env file in the temporary directory
handler, err := os.OpenFile(".env", os.O_CREATE|os.O_WRONLY, 0655)
@ -89,12 +84,7 @@ func TestReadDotEnv(t *testing.T) {
t.Run("no file", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
os.Chdir(tmpDir)
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(tmpDir))
})
require.NoError(t, os.Chdir(tmpDir))
e := readDotEnv(log)

View file

@ -21,11 +21,10 @@ func TestGenerateEbook(t *testing.T) {
t.Run("Successful ebook generate", func(t *testing.T) {
t.Run("valid bookmarkId that return HasEbook true", func(t *testing.T) {
// test cae
tempDir := t.TempDir()
dstDir := t.TempDir()
dstFile := "/ebook/1.epub"
tmpDir := t.TempDir()
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), dstDir))
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), tmpDir))
mockRequest := core.ProcessRequest{
Bookmark: model.BookmarkDTO{
@ -34,19 +33,19 @@ func TestGenerateEbook(t *testing.T) {
HTML: "<html><body>Example HTML</body></html>",
HasEbook: false,
},
DataDir: dstDir,
DataDir: tmpDir,
ContentType: "text/html",
}
bookmark, err := core.GenerateEbook(deps, mockRequest, fp.Join(tempDir, "1"))
bookmark, err := core.GenerateEbook(deps, mockRequest, dstFile)
assert.True(t, bookmark.HasEbook)
assert.NoError(t, err)
})
t.Run("ebook generate with valid BookmarkID EbookExist ImagePathExist ReturnWithHasEbookTrue", func(t *testing.T) {
dstDir := t.TempDir()
dstFile := "/ebook/2.epub"
tmpDir := t.TempDir()
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), dstDir))
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), tmpDir))
bookmark := model.BookmarkDTO{
ID: 2,
@ -54,29 +53,30 @@ func TestGenerateEbook(t *testing.T) {
}
mockRequest := core.ProcessRequest{
Bookmark: bookmark,
DataDir: dstDir,
DataDir: tmpDir,
ContentType: "text/html",
}
// Create the thumbnail file
imagePath := model.GetThumbnailPath(&bookmark)
deps.Domains.Storage.FS().MkdirAll(fp.Dir(imagePath), os.ModePerm)
imagedirPath := fp.Dir(imagePath)
deps.Domains.Storage.FS().MkdirAll(imagedirPath, os.ModePerm)
file, err := deps.Domains.Storage.FS().Create(imagePath)
if err != nil {
t.Fatal(err)
}
defer file.Close()
bookmark, err = core.GenerateEbook(deps, mockRequest, model.GetEbookPath(&bookmark))
expectedImagePath := "/bookmark/2/thumb"
bookmark, err = core.GenerateEbook(deps, mockRequest, dstFile)
expectedImagePath := string(fp.Separator) + fp.Join("bookmark", "2", "thumb")
assert.NoError(t, err)
assert.True(t, bookmark.HasEbook)
assert.Equalf(t, expectedImagePath, bookmark.ImageURL, "Expected imageURL %s, but got %s", expectedImagePath, bookmark.ImageURL)
})
t.Run("generate ebook valid BookmarkID EbookExist ReturnHasArchiveTrue", func(t *testing.T) {
tempDir := t.TempDir()
dstDir := t.TempDir()
dstFile := "/ebook/3.epub"
tmpDir := t.TempDir()
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), dstDir))
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), tmpDir))
bookmark := model.BookmarkDTO{
ID: 3,
@ -84,47 +84,50 @@ func TestGenerateEbook(t *testing.T) {
}
mockRequest := core.ProcessRequest{
Bookmark: bookmark,
DataDir: dstDir,
DataDir: tmpDir,
ContentType: "text/html",
}
// Create the archive file
archivePath := model.GetArchivePath(&bookmark)
deps.Domains.Storage.FS().MkdirAll(fp.Dir(archivePath), os.ModePerm)
archiveDirPath := fp.Dir(archivePath)
deps.Domains.Storage.FS().MkdirAll(archiveDirPath, os.ModePerm)
file, err := deps.Domains.Storage.FS().Create(archivePath)
if err != nil {
t.Fatal(err)
}
defer file.Close()
bookmark, err = core.GenerateEbook(deps, mockRequest, fp.Join(tempDir, "1"))
bookmark, err = core.GenerateEbook(deps, mockRequest, fp.Join(dstFile, "1"))
assert.True(t, bookmark.HasArchive)
assert.NoError(t, err)
})
})
t.Run("specific ebook generate case", func(t *testing.T) {
t.Run("invalid bookmarkId that return Error", func(t *testing.T) {
tempDir := t.TempDir()
dstFile := "/ebook/0.epub"
tmpDir := t.TempDir()
mockRequest := core.ProcessRequest{
Bookmark: model.BookmarkDTO{
ID: 0,
HasEbook: false,
},
DataDir: tempDir,
DataDir: tmpDir,
ContentType: "text/html",
}
bookmark, err := core.GenerateEbook(deps, mockRequest, tempDir)
bookmark, err := core.GenerateEbook(deps, mockRequest, dstFile)
assert.Equal(t, model.BookmarkDTO{
ID: 0,
HasEbook: false,
}, bookmark)
assert.Error(t, err)
assert.EqualError(t, err, "bookmark ID is not valid")
})
t.Run("ebook exist return HasEbook true", func(t *testing.T) {
dstDir := t.TempDir()
dstFile := "/ebook/1.epub"
tmpDir := t.TempDir()
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), dstDir))
deps.Domains.Storage = domains.NewStorageDomain(deps, afero.NewBasePathFs(afero.NewOsFs(), tmpDir))
bookmark := model.BookmarkDTO{
ID: 1,
@ -132,40 +135,42 @@ func TestGenerateEbook(t *testing.T) {
}
mockRequest := core.ProcessRequest{
Bookmark: bookmark,
DataDir: dstDir,
DataDir: tmpDir,
ContentType: "text/html",
}
// Create the ebook file
ebookFilePath := model.GetEbookPath(&bookmark)
deps.Domains.Storage.FS().MkdirAll(fp.Dir(ebookFilePath), os.ModePerm)
file, err := deps.Domains.Storage.FS().Create(ebookFilePath)
ebookPath := model.GetEbookPath(&bookmark)
ebookDirPath := fp.Dir(ebookPath)
deps.Domains.Storage.FS().MkdirAll(ebookDirPath, os.ModePerm)
file, err := deps.Domains.Storage.FS().Create(ebookPath)
if err != nil {
t.Fatal(err)
}
defer file.Close()
bookmark, err = core.GenerateEbook(deps, mockRequest, ebookFilePath)
bookmark, err = core.GenerateEbook(deps, mockRequest, dstFile)
assert.True(t, bookmark.HasEbook)
assert.NoError(t, err)
})
t.Run("generate ebook valid BookmarkID RetuenError for PDF file", func(t *testing.T) {
tempDir := t.TempDir()
dstFile := "/ebook/1.epub"
tmpDir := t.TempDir()
mockRequest := core.ProcessRequest{
Bookmark: model.BookmarkDTO{
ID: 1,
HasEbook: false,
},
DataDir: tempDir,
DataDir: tmpDir,
ContentType: "application/pdf",
}
bookmark, err := core.GenerateEbook(deps, mockRequest, tempDir)
bookmark, err := core.GenerateEbook(deps, mockRequest, dstFile)
assert.False(t, bookmark.HasEbook)
assert.Error(t, err)
assert.Contains(t, err.Error(), "can't create ebook for pdf")
assert.EqualError(t, err, "can't create ebook for pdf")
})
})
}

View file

@ -3,8 +3,6 @@ package core_test
import (
"bytes"
"context"
"net/http"
"net/http/httptest"
"os"
fp "path/filepath"
"testing"
@ -14,6 +12,7 @@ import (
"github.com/go-shiori/shiori/internal/testutil"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDownloadBookImage(t *testing.T) {
@ -24,51 +23,47 @@ func TestDownloadBookImage(t *testing.T) {
t.Run("fails", func(t *testing.T) {
// images is too small with unsupported format with a valid URL
imageURL := "https://github.com/go-shiori/shiori/blob/master/internal/view/assets/res/apple-touch-icon-152x152.png"
tempDir := t.TempDir()
dstPath := fp.Join(tempDir, "1")
defer os.Remove(dstPath)
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
dstFile := fp.Join(tmpDir, "image.png")
// Act
err := core.DownloadBookImage(deps, imageURL, dstPath)
err = core.DownloadBookImage(deps, imageURL, dstFile)
// Assert
assert.EqualError(t, err, "unsupported image type")
assert.False(t, deps.Domains.Storage.FileExists(dstPath))
assert.False(t, deps.Domains.Storage.FileExists(dstFile))
})
t.Run("successful download image", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
require.NoError(t, os.Chdir(tmpDir))
// Arrange
imageURL := "https://raw.githubusercontent.com/go-shiori/shiori/master/docs/readme/cover.png"
tempDir := t.TempDir()
dstPath := fp.Join(tempDir, "1")
defer os.Remove(dstPath)
dstFile := "." + string(fp.Separator) + "cover.png"
// Act
err := core.DownloadBookImage(deps, imageURL, dstPath)
err = core.DownloadBookImage(deps, imageURL, dstFile)
// Assert
assert.NoError(t, err)
assert.True(t, deps.Domains.Storage.FileExists(dstPath))
assert.True(t, deps.Domains.Storage.FileExists(dstFile))
})
t.Run("successful download medium size image", func(t *testing.T) {
// create a file server handler for the 'testdata' directory
fs := http.FileServer(http.Dir("../../testdata/"))
// start a test server with the file server handler
server := httptest.NewServer(fs)
defer server.Close()
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
require.NoError(t, os.Chdir(tmpDir))
// Arrange
imageURL := server.URL + "/medium_image.png"
tempDir := t.TempDir()
dstPath := fp.Join(tempDir, "1")
defer os.Remove(dstPath)
imageURL := "https://raw.githubusercontent.com/go-shiori/shiori/master/testdata/medium_image.png"
dstFile := "." + string(fp.Separator) + "medium_image.png"
// Act
err := core.DownloadBookImage(deps, imageURL, dstPath)
err = core.DownloadBookImage(deps, imageURL, dstFile)
// Assert
assert.NoError(t, err)
assert.True(t, deps.Domains.Storage.FileExists(dstPath))
assert.True(t, deps.Domains.Storage.FileExists(dstFile))
})
})
}
@ -78,6 +73,7 @@ func TestProcessBookmark(t *testing.T) {
_, deps := testutil.GetTestConfigurationAndDependencies(t, context.TODO(), logger)
t.Run("ProcessRequest with sucssesful result", func(t *testing.T) {
tmpDir := t.TempDir()
t.Run("Normal without image", func(t *testing.T) {
bookmark := model.BookmarkDTO{
ID: 1,
@ -92,7 +88,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}
@ -112,7 +108,7 @@ func TestProcessBookmark(t *testing.T) {
}
})
t.Run("Normal with multipleimage", func(t *testing.T) {
tmpDir := t.TempDir()
html := `html<html>
<head>
<meta property="og:image" content="http://example.com/image1.jpg">
@ -136,7 +132,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}
@ -156,18 +152,12 @@ func TestProcessBookmark(t *testing.T) {
}
})
t.Run("ProcessRequest sucssesful with multipleimage included favicon and Thumbnail ", func(t *testing.T) {
// create a file server handler for the 'testdata' directory
fs := http.FileServer(http.Dir("../../testdata/"))
// start a test server with the file server handler
server := httptest.NewServer(fs)
defer server.Close()
tmpDir := t.TempDir()
html := `html<html>
<head>
<meta property="og:image" content="http://example.com/image1.jpg">
<meta property="og:image" content="` + server.URL + `/big_image.png">
<link rel="icon" type="image/svg" href="` + server.URL + `/favicon.svg">
<meta property="og:image" content="https://raw.githubusercontent.com/go-shiori/shiori/master/testdata/big_image.png">
<link rel="icon" type="image/svg" href="https://raw.githubusercontent.com/go-shiori/shiori/master/testdata/favicon.svg">
</head>
<body>
<p>This is an example article</p>
@ -186,12 +176,12 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}
expected, _, _ := core.ProcessBookmark(deps, request)
assert.True(t, deps.Domains.Storage.FileExists(fp.Join("thumb", "1")))
if expected.ID != bookmark.ID {
t.Errorf("Unexpected ID: got %v, want %v", expected.ID, bookmark.ID)
}
@ -206,6 +196,7 @@ func TestProcessBookmark(t *testing.T) {
}
})
t.Run("ProcessRequest sucssesful with empty title ", func(t *testing.T) {
tmpDir := t.TempDir()
bookmark := model.BookmarkDTO{
ID: 1,
URL: "https://example.com",
@ -219,7 +210,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}
@ -239,6 +230,7 @@ func TestProcessBookmark(t *testing.T) {
}
})
t.Run("ProcessRequest sucssesful with empty Excerpt", func(t *testing.T) {
tmpDir := t.TempDir()
bookmark := model.BookmarkDTO{
ID: 1,
URL: "https://example.com",
@ -252,7 +244,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: false,
}
@ -272,6 +264,7 @@ func TestProcessBookmark(t *testing.T) {
}
})
t.Run("Specific case", func(t *testing.T) {
tmpDir := t.TempDir()
t.Run("ProcessRequest with ID zero", func(t *testing.T) {
bookmark := model.BookmarkDTO{
@ -287,7 +280,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "text/html",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}
@ -298,7 +291,7 @@ func TestProcessBookmark(t *testing.T) {
})
t.Run("ProcessRequest that content type not zero", func(t *testing.T) {
tmpDir := t.TempDir()
bookmark := model.BookmarkDTO{
ID: 1,
URL: "https://example.com",
@ -312,7 +305,7 @@ func TestProcessBookmark(t *testing.T) {
Bookmark: bookmark,
Content: content,
ContentType: "application/pdf",
DataDir: "/tmp",
DataDir: tmpDir,
KeepTitle: true,
KeepExcerpt: true,
}

View file

@ -6,10 +6,11 @@ import (
"github.com/go-shiori/shiori/internal/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type databaseTestCase func(t *testing.T, db DB)
type testDatabaseFactory func(ctx context.Context) (DB, error)
type testDatabaseFactory func(t *testing.T, ctx context.Context) (DB, error)
func testDatabase(t *testing.T, dbFactory testDatabaseFactory) {
tests := map[string]databaseTestCase{
@ -30,12 +31,17 @@ func testDatabase(t *testing.T, dbFactory testDatabaseFactory) {
// Tags
"testCreateTag": testCreateTag,
"testCreateTags": testCreateTags,
// Accounts
"testSaveAccount": testSaveAccount,
"testSaveAccountSetting": testSaveAccountSettings,
"testGetAccount": testGetAccount,
"testGetAccounts": testGetAccounts,
}
for testName, testCase := range tests {
t.Run(testName, func(tInner *testing.T) {
ctx := context.TODO()
db, err := dbFactory(ctx)
db, err := dbFactory(t, ctx)
assert.NoError(tInner, err, "Error recreating database")
testCase(tInner, db)
})
@ -323,3 +329,98 @@ func testCreateTags(t *testing.T, db DB) {
err := db.CreateTags(ctx, model.Tag{Name: "shiori"}, model.Tag{Name: "shiori2"})
assert.NoError(t, err, "Save tag must not fail")
}
func testSaveAccount(t *testing.T, db DB) {
ctx := context.TODO()
t.Run("success", func(t *testing.T) {
acc := model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
err := db.SaveAccount(ctx, acc)
require.Nil(t, err)
})
}
func testSaveAccountSettings(t *testing.T, db DB) {
ctx := context.TODO()
t.Run("success", func(t *testing.T) {
acc := model.Account{
Username: "test",
Config: model.UserConfig{},
}
err := db.SaveAccountSettings(ctx, acc)
require.Nil(t, err)
})
}
func testGetAccount(t *testing.T, db DB) {
ctx := context.TODO()
t.Run("success", func(t *testing.T) {
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
// Successful case
account, exists, err := db.GetAccount(ctx, acc.Username)
assert.Nil(t, err)
assert.True(t, exists, "Expected account to exist")
assert.Equal(t, acc.Username, account.Username)
}
// Falid case
account, exists, err := db.GetAccount(ctx, "foobar")
assert.NotNil(t, err)
assert.False(t, exists, "Expected account to exist")
assert.Empty(t, account.Username)
})
}
func testGetAccounts(t *testing.T, db DB) {
ctx := context.TODO()
t.Run("success", func(t *testing.T) {
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
}
// Successful case
// without opt
accounts, err := db.GetAccounts(ctx, GetAccountsOptions{})
assert.NoError(t, err)
assert.Equal(t, 3, len(accounts))
// with owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Owner: true})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with opt
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "foo"})
assert.NoError(t, err)
assert.Equal(t, 2, len(accounts))
// with opt and owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "hello", Owner: false})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with not result
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "shiori"})
assert.NoError(t, err)
assert.Equal(t, 0, len(accounts))
})
}

View file

@ -1,17 +1,17 @@
//go:build !test_sqlite_only
// +build !test_sqlite_only
package database
import (
"context"
"log"
"os"
"path/filepath"
"testing"
"github.com/go-shiori/shiori/internal/model"
"github.com/golang-migrate/migrate/v4"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func init() {
@ -21,7 +21,7 @@ func init() {
}
}
func mysqlTestDatabaseFactory(ctx context.Context) (DB, error) {
func mysqlTestDatabaseFactory(_ *testing.T, ctx context.Context) (DB, error) {
connString := os.Getenv("SHIORI_TEST_MYSQL_URL")
db, err := OpenMySQLDatabase(ctx, connString)
if err != nil {
@ -61,117 +61,3 @@ func mysqlTestDatabaseFactory(ctx context.Context) (DB, error) {
func TestMysqlsDatabase(t *testing.T) {
testDatabase(t, mysqlTestDatabaseFactory)
}
func TestSaveAccountSettingsMySql(t *testing.T) {
ctx := context.TODO()
db, err := mysqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Mock data
account := model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
// Successful case
err = db.SaveAccountSettings(ctx, account)
assert.NoError(t, err)
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
account = model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
err = db.SaveAccountSettings(ctx, account)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestGetAccountsMySql(t *testing.T) {
ctx := context.TODO()
db, err := mysqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
}
// Successful case
// without opt
accounts, err := db.GetAccounts(ctx, GetAccountsOptions{})
assert.NoError(t, err)
assert.Equal(t, 3, len(accounts))
// with owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Owner: true})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with opt
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "foo"})
assert.NoError(t, err)
assert.Equal(t, 2, len(accounts))
// with opt and owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "hello", Owner: false})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with not result
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "shiori"})
assert.NoError(t, err)
assert.Equal(t, 0, len(accounts))
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
// with invalid query
opts := GetAccountsOptions{Keyword: "foo", Owner: true}
_, err = db.GetAccounts(ctx, opts)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestGetAccountMySql(t *testing.T) {
ctx := context.TODO()
db, err := mysqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
// Successful case
account, exists, err := db.GetAccount(ctx, acc.Username)
assert.Nil(t, err)
assert.True(t, exists, "Expected account to exist")
assert.Equal(t, acc.Username, account.Username)
}
// Falid case
account, exists, err := db.GetAccount(ctx, "foobar")
assert.NotNil(t, err)
assert.False(t, exists, "Expected account to exist")
assert.Empty(t, account.Username)
}

View file

@ -1,3 +1,6 @@
//go:build !test_sqlite_only
// +build !test_sqlite_only
package database
import (
@ -5,12 +8,9 @@ import (
"errors"
"log"
"os"
"path/filepath"
"testing"
"github.com/go-shiori/shiori/internal/model"
"github.com/golang-migrate/migrate/v4"
"github.com/stretchr/testify/assert"
)
func init() {
@ -20,7 +20,7 @@ func init() {
}
}
func postgresqlTestDatabaseFactory(ctx context.Context) (DB, error) {
func postgresqlTestDatabaseFactory(_ *testing.T, ctx context.Context) (DB, error) {
db, err := OpenPGDatabase(ctx, os.Getenv("SHIORI_TEST_PG_URL"))
if err != nil {
return nil, err
@ -41,116 +41,3 @@ func postgresqlTestDatabaseFactory(ctx context.Context) (DB, error) {
func TestPostgresDatabase(t *testing.T) {
testDatabase(t, postgresqlTestDatabaseFactory)
}
func TestSaveAccountSettingsPg(t *testing.T) {
ctx := context.TODO()
db, err := postgresqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Mock data
account := model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
// Successful case
err = db.SaveAccountSettings(ctx, account)
assert.NoError(t, err)
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
account = model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
err = db.SaveAccountSettings(ctx, account)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestGetAccountsPg(t *testing.T) {
ctx := context.TODO()
db, err := postgresqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
}
// Successful case
// without opt
accounts, err := db.GetAccounts(ctx, GetAccountsOptions{})
assert.NoError(t, err)
assert.Equal(t, 3, len(accounts))
// with owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Owner: true})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with opt
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "foo"})
assert.NoError(t, err)
assert.Equal(t, 2, len(accounts))
// with opt and owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "hello", Owner: false})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with not result
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "shiori"})
assert.NoError(t, err)
assert.Equal(t, 0, len(accounts))
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
// with invalid query
opts := GetAccountsOptions{Keyword: "foo", Owner: true}
_, err = db.GetAccounts(ctx, opts)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestGetAccountPg(t *testing.T) {
ctx := context.TODO()
db, err := postgresqlTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
// Successful case
account, exists, err := db.GetAccount(ctx, acc.Username)
assert.Nil(t, err)
assert.True(t, exists, "Expected account to exist")
assert.Equal(t, acc.Username, account.Username)
}
// Falid case
account, exists, err := db.GetAccount(ctx, "foobar")
assert.NotNil(t, err)
assert.False(t, exists, "Expected account to exist")
assert.Empty(t, account.Username)
}

View file

@ -10,18 +10,14 @@ import (
"github.com/golang-migrate/migrate/v4"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var sqliteDatabaseTestPath string
func sqliteTestDatabaseFactory(t *testing.T, ctx context.Context) (DB, error) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
func init() {
sqliteDatabaseTestPath = filepath.Join(os.TempDir(), "shiori.db")
}
func sqliteTestDatabaseFactory(ctx context.Context) (DB, error) {
os.Remove(sqliteDatabaseTestPath)
db, err := OpenSQLiteDatabase(ctx, sqliteDatabaseTestPath)
db, err := OpenSQLiteDatabase(ctx, filepath.Join(tmpDir, "shiori.db"))
if err != nil {
return nil, err
}
@ -47,7 +43,7 @@ func TestSqliteDatabase(t *testing.T) {
func testSqliteGetBookmarksWithDash(t *testing.T) {
ctx := context.TODO()
db, err := sqliteTestDatabaseFactory(ctx)
db, err := sqliteTestDatabaseFactory(t, ctx)
assert.NoError(t, err)
book := model.BookmarkDTO{
@ -76,105 +72,3 @@ func testSqliteGetBookmarksWithDash(t *testing.T) {
assert.Equal(t, savedBookmark.ID, results[0].ID, "bookmark should be the one saved")
}
func TestSQLiteDatabase_SaveAccount(t *testing.T) {
ctx := context.TODO()
// Initialize not correct database
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err := factory(ctx)
assert.Nil(t, err)
// Test falid database
acc := model.Account{}
err = db.SaveAccount(ctx, acc)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestSaveAccountSettings(t *testing.T) {
ctx := context.TODO()
db, err := sqliteTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Mock data
account := model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
// Successful case
err = db.SaveAccountSettings(ctx, account)
assert.NoError(t, err)
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
account = model.Account{
Username: "testuser",
Config: model.UserConfig{},
}
err = db.SaveAccountSettings(ctx, account)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}
func TestGetAccounts(t *testing.T) {
ctx := context.TODO()
db, err := sqliteTestDatabaseFactory(ctx)
assert.NoError(t, err)
// Insert test accounts
testAccounts := []model.Account{
{Username: "foo", Password: "bar", Owner: false},
{Username: "hello", Password: "world", Owner: false},
{Username: "foo_bar", Password: "foobar", Owner: true},
}
for _, acc := range testAccounts {
err := db.SaveAccount(ctx, acc)
assert.Nil(t, err)
}
// Successful case
// without opt
accounts, err := db.GetAccounts(ctx, GetAccountsOptions{})
assert.NoError(t, err)
assert.Equal(t, 3, len(accounts))
// with owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Owner: true})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with opt
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "foo"})
assert.NoError(t, err)
assert.Equal(t, 2, len(accounts))
// with opt and owner
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "hello", Owner: false})
assert.NoError(t, err)
assert.Equal(t, 1, len(accounts))
// with not result
accounts, err = db.GetAccounts(ctx, GetAccountsOptions{Keyword: "shiori"})
assert.NoError(t, err)
assert.Equal(t, 0, len(accounts))
// Initialize not correct database
ctx = context.TODO()
factory := func(ctx context.Context) (DB, error) {
return OpenSQLiteDatabase(ctx, filepath.Join(os.TempDir(), "shiori_test.db"))
}
db, err = factory(ctx)
assert.Nil(t, err)
// with invalid query
opts := GetAccountsOptions{Keyword: "foo", Owner: true}
_, err = db.GetAccounts(ctx, opts)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "SQL logic error: no such table: account (1)")
}

View file

@ -3,7 +3,7 @@ package routes
import (
"embed"
"net/http"
"path/filepath"
"path"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
@ -27,7 +27,7 @@ func (fs assetsFS) Exists(prefix string, path string) bool {
}
func (fs assetsFS) Open(name string) (http.File, error) {
f, err := fs.FileSystem.Open(filepath.Join("assets", name))
f, err := fs.FileSystem.Open(path.Join("assets", name))
if err != nil {
logrus.WithError(err).WithField("path", name).Error("requested frontend file not found")
}

View file

@ -21,18 +21,21 @@ func GetTestConfigurationAndDependencies(t *testing.T, ctx context.Context, logg
tmp, err := os.CreateTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
os.Remove(tmp.Name())
})
cfg := config.ParseServerConfiguration(ctx, logger)
cfg.Http.SecretKey = []byte("test")
tempDir, err := os.MkdirTemp("", "")
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
db, err := database.OpenSQLiteDatabase(ctx, tmp.Name())
require.NoError(t, err)
require.NoError(t, db.Migrate())
cfg.Storage.DataDir = tempDir
cfg.Storage.DataDir = tmpDir
deps := dependencies.NewDependencies(logger, db, cfg)
deps.Database = db

View file

@ -1,13 +1,12 @@
#!/bin/bash
# Check if gotestfmt is installed
GOTESTFMT=$(which gotestfmt)
if [[ -z "${GOTESTFMT}" ]]; then
if ! [ -x "$(command -v gotestfmt)" ]; then
echo "gotestfmt not found. Using test standard output."
fi
# if gotestfmt is installed, run with it
if [[ -n "${GOTESTFMT}" ]]; then
if [ -x "$(command -v gotestfmt)" ]; then
set -o pipefail
go test ./... ${GO_TEST_FLAGS} -json | gotestfmt ${GOTESTFMT_FLAGS}
else