mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
feat(database): setup integration tests (#1118)
* refactor: this should be Debug() just like the rest. * feat: catch error when updating client table. Before if we provided the wrong ID it will just say it's successful when it shouldn't. * chore: handle the errors. * fix: defer tx.Rollback(). When I try handling the error we always hit the error no matter what even though there wasn't any error, This is due that defer block being executed unconditionally so even after we commit it successfully it will just give error. So add checking then commit it if all good. * feat: added testing env. This way we can use in memory sqlite. * chore: Delete log should be debug as well. * feat: enable foreign keys for testing for sqlite. I recommend enabling all together. Not sure why it's commented but for now will keep it the same and only enable for testing. * chore: catch error, if deleting a record fails. * chore: catch error, if deleting a record fails. * chore: catch error, when failed to enable toggle. * chore: catch error, if updating failed. * chore(filter): catch error, if deleting failed. * chore(filter): catch error, if row is not modified for ToggleEnabled. * chore(feed): Should be debug level to match with others. * chore(feed): catch error when nothing is updated. * chore: update docker-compose.yml add test_db for postgres. * chore(ci): update include postgres db service before running tests. * feat(database): Added database testing. * feat(database): Added api integration testing. * feat(database): Added action integration testing. * feat(database): Added download_client integration testing. * feat(database): Added filter integration testing. * test(database): initial tests model (WIP) * chore(feed): handle error when nothing is deleted. * tests(feed): added delete testing. * chore(feed): handle error when nothing is updated. * chore(feed): handle error when nothing is updated. * chore(feed): handle error when nothing is updated. * feat(database): Added feed integration testing. * fix(feed_cache): This should be time.Time not time.Duration. * chore(feed_cache): handle error when deleting fails. * feat(database): Added feed_cache integration testing. * chore: add EOL * feat: added indexer_test.go * feat: added mock irc data * fix: the column is not pass anymore it's password. * chore: added assertion. * fix: This is password column not pass test is failing because of it. * feat: added tests cases for irc. * feat: added test cases for release. * feat: added test cases for notifications. * feat: added Delete to the User DB that way it can be used for testing. * feat: added user database tests. * refactor: Make setupLogger and setupDatabase private also renamed them. Changed the visibility of `setupLogger` to private based on feedback. Also renamed the function to `setupLoggerForTest` and `setupDatabaseForTest` to make its purpose more descriptive. * fix(database): tests postgres ssl mode disable * refactor(database): setup and teardown --------- Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
This commit is contained in:
parent
5d6fc84f4c
commit
666bdf68cd
26 changed files with 4676 additions and 44 deletions
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
@ -55,6 +55,16 @@ jobs:
|
|||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
test_postgres:
|
||||
image: postgres:12.10
|
||||
ports:
|
||||
- "5437:5432"
|
||||
env:
|
||||
POSTGRES_USER: testdb
|
||||
POSTGRES_PASSWORD: testdb
|
||||
POSTGRES_DB: autobrr
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
@ -20,7 +20,19 @@ services:
|
|||
- POSTGRES_USER=autobrr
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=autobrr
|
||||
test_postgres:
|
||||
image: postgres:12.10
|
||||
container_name: autobrr_postgres_test
|
||||
volumes:
|
||||
- test_postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5437:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=testdb
|
||||
- POSTGRES_PASSWORD=testdb
|
||||
- POSTGRES_DB=autobrr
|
||||
|
||||
|
||||
volumes:
|
||||
postgres:
|
||||
postgres:
|
||||
test_postgres:
|
||||
|
|
|
@ -402,10 +402,17 @@ func (r *ActionRepo) Delete(ctx context.Context, req *domain.DeleteActionRequest
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
if _, err = r.db.handler.ExecContext(ctx, query, args...); err != nil {
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("action.delete: %v", req.ActionId)
|
||||
|
||||
return nil
|
||||
|
@ -421,10 +428,17 @@ func (r *ActionRepo) DeleteByFilterID(ctx context.Context, filterID int) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
if _, err := r.db.handler.ExecContext(ctx, query, args...); err != nil {
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("action.deleteByFilterID: %v", filterID)
|
||||
|
||||
return nil
|
||||
|
@ -715,10 +729,17 @@ func (r *ActionRepo) ToggleEnabled(actionID int) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
if _, err := r.db.handler.Exec(query, args...); err != nil {
|
||||
result, err := r.db.handler.Exec(query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("action.toggleEnabled: %v", actionID)
|
||||
|
||||
return nil
|
||||
|
|
531
internal/database/action_test.go
Normal file
531
internal/database/action_test.go
Normal file
|
@ -0,0 +1,531 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockAction() domain.Action {
|
||||
return domain.Action{
|
||||
Name: "randomAction",
|
||||
Type: domain.ActionTypeTest,
|
||||
Enabled: true,
|
||||
ExecCmd: "/home/user/Downloads/test.sh",
|
||||
ExecArgs: "WGET_URL",
|
||||
WatchFolder: "/home/user/Downloads",
|
||||
Category: "HD, 720p",
|
||||
Tags: "P2P, x264",
|
||||
Label: "testLabel",
|
||||
SavePath: "/home/user/Downloads",
|
||||
Paused: false,
|
||||
IgnoreRules: false,
|
||||
SkipHashCheck: false,
|
||||
ContentLayout: domain.ActionContentLayoutOriginal,
|
||||
LimitUploadSpeed: 0,
|
||||
LimitDownloadSpeed: 0,
|
||||
LimitRatio: 0,
|
||||
LimitSeedTime: 0,
|
||||
ReAnnounceSkip: false,
|
||||
ReAnnounceDelete: false,
|
||||
ReAnnounceInterval: 0,
|
||||
ReAnnounceMaxAttempts: 0,
|
||||
WebhookHost: "http://localhost:8080",
|
||||
WebhookType: "test",
|
||||
WebhookMethod: "POST",
|
||||
WebhookData: "testData",
|
||||
WebhookHeaders: []string{"testHeader"},
|
||||
ExternalDownloadClientID: 21,
|
||||
FilterID: 1,
|
||||
ClientID: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Actual test for Store
|
||||
createdAction, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdAction)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds_With_Missing_or_empty_fields [%s]", dbType), func(t *testing.T) {
|
||||
mockData := domain.Action{}
|
||||
createdAction, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Invalid_ClientID [%s]", dbType), func(t *testing.T) {
|
||||
mockData := getMockAction()
|
||||
mockData.ClientID = 9999
|
||||
_, err := repo.Store(context.Background(), mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
mockData := getMockAction()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
_, err := repo.Store(ctx, mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_StoreFilterActions(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterActions_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Actual test for StoreFilterActions
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdActions)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterActions_Fails_Invalid_FilterID [%s]", dbType), func(t *testing.T) {
|
||||
_, err := repo.StoreFilterActions(context.Background(), 9999, []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterActions_Fails_Empty_Actions_Array [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
_, err = repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterActions_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
_, err = repo.StoreFilterActions(ctx, int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_FindByFilterID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for FindByFilterID
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actions)
|
||||
assert.Equal(t, 1, len(actions))
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Fails_No_Actions [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
// Actual test for FindByFilterID
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(actions))
|
||||
|
||||
// Cleanup
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Succeeds_With_Invalid_FilterID [%s]", dbType), func(t *testing.T) {
|
||||
actions, err := repo.FindByFilterID(context.Background(), 9999) // 9999 is an invalid filter ID
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actions)
|
||||
assert.Equal(t, 0, len(actions))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
actions, err := repo.FindByFilterID(ctx, 1)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, actions)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_List(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for List
|
||||
actions, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actions)
|
||||
assert.GreaterOrEqual(t, len(actions), 1)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
actions, err := repo.List(ctx)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, actions)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_Get(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for Get
|
||||
action, err := repo.Get(context.Background(), &domain.GetActionRequest{Id: createdActions[0].ID})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, action)
|
||||
assert.Equal(t, createdActions[0].ID, action.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
action, err := repo.Get(context.Background(), &domain.GetActionRequest{Id: 9999})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, domain.ErrRecordNotFound, err)
|
||||
assert.Nil(t, action)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
action, err := repo.Get(ctx, &domain.GetActionRequest{Id: 1})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, action)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for Delete
|
||||
err = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the record was actually deleted
|
||||
action, err := repo.Get(context.Background(), &domain.GetActionRequest{Id: createdActions[0].ID})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, domain.ErrRecordNotFound, err)
|
||||
assert.Nil(t, action)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: 9999})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := repo.Delete(ctx, &domain.DeleteActionRequest{ActionId: 1})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_DeleteByFilterID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteByFilterID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.DeleteByFilterID(context.Background(), mockData.FilterID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that actions with the given filterID are actually deleted
|
||||
action, err := repo.Get(context.Background(), &domain.GetActionRequest{Id: createdActions[0].ID})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, domain.ErrRecordNotFound, err)
|
||||
assert.Nil(t, action)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteByFilterID_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.DeleteByFilterID(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteByFilterID_Fails_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := repo.DeleteByFilterID(ctx, mockData.FilterID)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRepo_ToggleEnabled(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
repo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
mockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
mockData.Enabled = false
|
||||
createdActions, err := repo.StoreFilterActions(context.Background(), int64(createdFilters[0].ID), []*domain.Action{&mockData})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for ToggleEnabled
|
||||
err = repo.ToggleEnabled(createdActions[0].ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the record was actually updated
|
||||
action, err := repo.Get(context.Background(), &domain.GetActionRequest{Id: createdActions[0].ID})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, action.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdActions[0].ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.ToggleEnabled(9999)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
88
internal/database/api_test.go
Normal file
88
internal/database/api_test.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPIRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewAPIRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds_With_Valid_Key [%s]", dbType), func(t *testing.T) {
|
||||
key := &domain.APIKey{Name: "TestKey", Key: "123", Scopes: []string{"read", "write"}}
|
||||
err := repo.Store(context.Background(), key)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, key.CreatedAt)
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), key.Key)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_If_No_Name_Or_Scopes [%s]", dbType), func(t *testing.T) {
|
||||
key := &domain.APIKey{Key: "456"}
|
||||
err := repo.Store(context.Background(), key)
|
||||
assert.Error(t, err) // Should fail when trying to insert a key without scopes (null constraint)
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), key.Key)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_If_Duplicate_Key [%s]", dbType), func(t *testing.T) {
|
||||
key := &domain.APIKey{Key: "789", Scopes: []string{}}
|
||||
err1 := repo.Store(context.Background(), key)
|
||||
err2 := repo.Store(context.Background(), key)
|
||||
assert.NoError(t, err1)
|
||||
assert.Error(t, err2) // Should fail when trying to insert a duplicate key
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), key.Key)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewAPIRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds_With_Existing_Key [%s]", dbType), func(t *testing.T) {
|
||||
key := &domain.APIKey{Name: "TestKey", Key: "123", Scopes: []string{"read", "write"}}
|
||||
_ = repo.Store(context.Background(), key)
|
||||
err := repo.Delete(context.Background(), key.Key)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds_If_Key_Does_Not_Exist [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.Delete(context.Background(), "nonexistent")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAPIRepo_GetKeys(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewAPIRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("GetKeys_Returns_Keys_If_Exists [%s]", dbType), func(t *testing.T) {
|
||||
key := &domain.APIKey{Name: "TestKey", Key: "123", Scopes: []string{"read", "write"}}
|
||||
_ = repo.Store(context.Background(), key)
|
||||
keys, err := repo.GetKeys(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, len(keys), 0)
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), key.Key)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("GetKeys_Returns_Empty_If_No_Keys [%s]", dbType), func(t *testing.T) {
|
||||
keys, err := repo.GetKeys(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(keys))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
@ -17,8 +18,6 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var databaseDriver = "sqlite"
|
||||
|
||||
type DB struct {
|
||||
log zerolog.Logger
|
||||
handler *sql.DB
|
||||
|
@ -36,25 +35,27 @@ func NewDB(cfg *domain.Config, log logger.Logger) (*DB, error) {
|
|||
db := &DB{
|
||||
// set default placeholder for squirrel to support both sqlite and postgres
|
||||
squirrel: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
|
||||
log: log.With().Str("module", "database").Logger(),
|
||||
log: log.With().Str("module", "database").Str("type", cfg.DatabaseType).Logger(),
|
||||
}
|
||||
db.ctx, db.cancel = context.WithCancel(context.Background())
|
||||
|
||||
switch cfg.DatabaseType {
|
||||
case "sqlite":
|
||||
databaseDriver = "sqlite"
|
||||
db.Driver = "sqlite"
|
||||
db.DSN = dataSourceName(cfg.ConfigPath, "autobrr.db")
|
||||
if os.Getenv("IS_TEST_ENV") == "true" {
|
||||
db.DSN = ":memory:"
|
||||
} else {
|
||||
db.DSN = dataSourceName(cfg.ConfigPath, "autobrr.db")
|
||||
}
|
||||
case "postgres":
|
||||
if cfg.PostgresHost == "" || cfg.PostgresPort == 0 || cfg.PostgresDatabase == "" {
|
||||
return nil, errors.New("postgres: bad variables")
|
||||
}
|
||||
db.DSN = fmt.Sprintf("postgres://%v:%v@%v:%d/%v?sslmode=%v", cfg.PostgresUser, cfg.PostgresPass, cfg.PostgresHost, cfg.PostgresPort, cfg.PostgresDatabase, cfg.PostgresSSLMode)
|
||||
if cfg.PostgresExtraParams != "" {
|
||||
db.DSN = fmt.Sprintf("%s&%s", db.DSN, cfg.PostgresExtraParams)
|
||||
db.DSN = fmt.Sprintf("%s&%s", db.DSN, cfg.PostgresExtraParams)
|
||||
}
|
||||
db.Driver = "postgres"
|
||||
databaseDriver = "postgres"
|
||||
default:
|
||||
return nil, errors.New("unsupported database: %v", cfg.DatabaseType)
|
||||
}
|
||||
|
@ -93,7 +94,7 @@ func (db *DB) Close() error {
|
|||
}
|
||||
case "postgres":
|
||||
}
|
||||
|
||||
|
||||
// cancel background context
|
||||
db.cancel()
|
||||
|
||||
|
@ -131,8 +132,9 @@ type ILikeDynamic interface {
|
|||
|
||||
// ILike is a wrapper for sq.Like and sq.ILike
|
||||
// SQLite does not support ILike but postgres does so this checks what database is being used
|
||||
func ILike(col string, val string) ILikeDynamic {
|
||||
if databaseDriver == "sqlite" {
|
||||
func (db *DB) ILike(col string, val string) ILikeDynamic {
|
||||
//if databaseDriver == "sqlite" {
|
||||
if db.Driver == "sqlite" {
|
||||
return sq.Like{col: val}
|
||||
}
|
||||
|
||||
|
|
203
internal/database/database_test.go
Normal file
203
internal/database/database_test.go
Normal file
|
@ -0,0 +1,203 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/autobrr/autobrr/internal/logger"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getDbs() []string {
|
||||
return []string{"sqlite", "postgres"}
|
||||
}
|
||||
|
||||
var testDBs map[string]*DB
|
||||
|
||||
func setupDatabaseForTest(t *testing.T, dbType string) *DB {
|
||||
if d, ok := testDBs[dbType]; ok {
|
||||
return d
|
||||
}
|
||||
|
||||
err := os.Setenv("IS_TEST_ENV", "true")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not set env variable: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg := &domain.Config{
|
||||
LogLevel: "INFO",
|
||||
DatabaseType: dbType,
|
||||
PostgresHost: "localhost",
|
||||
PostgresPort: 5437,
|
||||
PostgresDatabase: "autobrr",
|
||||
PostgresUser: "testdb",
|
||||
PostgresPass: "testdb",
|
||||
PostgresSSLMode: "disable",
|
||||
}
|
||||
|
||||
// Init a new logger
|
||||
log := logger.New(cfg)
|
||||
|
||||
// Initialize a new DB connection
|
||||
db, err := NewDB(cfg, log)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create database: %v", err)
|
||||
}
|
||||
|
||||
// Open the database connection
|
||||
if err := db.Open(); err != nil {
|
||||
t.Fatalf("Could not open db connection: %v", err)
|
||||
}
|
||||
|
||||
testDBs[dbType] = db
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func setupPostgresForTest() *DB {
|
||||
dbtype := "postgres"
|
||||
if d, ok := testDBs[dbtype]; ok {
|
||||
return d
|
||||
}
|
||||
|
||||
cfg := &domain.Config{
|
||||
LogLevel: "INFO",
|
||||
DatabaseType: dbtype,
|
||||
PostgresHost: "localhost",
|
||||
PostgresPort: 5437,
|
||||
PostgresDatabase: "autobrr",
|
||||
PostgresUser: "testdb",
|
||||
PostgresPass: "testdb",
|
||||
PostgresSSLMode: "disable",
|
||||
}
|
||||
|
||||
// Init a new logger
|
||||
logr := logger.New(cfg)
|
||||
|
||||
logr.With().Str("type", "postgres").Logger()
|
||||
|
||||
// Initialize a new DB connection
|
||||
db, err := NewDB(cfg, logr)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create database: %q", err)
|
||||
}
|
||||
|
||||
// Open the database connection
|
||||
if db.handler, err = sql.Open("postgres", db.DSN); err != nil {
|
||||
log.Fatalf("could not open postgres connection: %q", err)
|
||||
}
|
||||
|
||||
if err = db.handler.Ping(); err != nil {
|
||||
log.Fatalf("could not ping postgres database: %q", err)
|
||||
}
|
||||
|
||||
// drop tables before migrate to always have a clean state
|
||||
if _, err := db.handler.Exec(`
|
||||
DROP SCHEMA public CASCADE;
|
||||
CREATE SCHEMA public;
|
||||
|
||||
-- Restore default permissions
|
||||
GRANT ALL ON SCHEMA public TO testdb;
|
||||
GRANT ALL ON SCHEMA public TO public;
|
||||
`); err != nil {
|
||||
log.Fatalf("Could not drop database: %q", err)
|
||||
}
|
||||
|
||||
// migrate db
|
||||
if err = db.migratePostgres(); err != nil {
|
||||
log.Fatalf("Could not migrate postgres database: %q", err)
|
||||
}
|
||||
|
||||
testDBs[dbtype] = db
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func setupSqliteForTest() *DB {
|
||||
dbtype := "sqlite"
|
||||
|
||||
if d, ok := testDBs[dbtype]; ok {
|
||||
return d
|
||||
}
|
||||
|
||||
cfg := &domain.Config{
|
||||
LogLevel: "INFO",
|
||||
DatabaseType: dbtype,
|
||||
}
|
||||
|
||||
// Init a new logger
|
||||
logr := logger.New(cfg)
|
||||
|
||||
// Initialize a new DB connection
|
||||
db, err := NewDB(cfg, logr)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create database: %v", err)
|
||||
}
|
||||
|
||||
// Open the database connection
|
||||
if err := db.Open(); err != nil {
|
||||
log.Fatalf("Could not open db connection: %v", err)
|
||||
}
|
||||
|
||||
testDBs[dbtype] = db
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func setupLoggerForTest() logger.Logger {
|
||||
cfg := &domain.Config{
|
||||
LogLevel: "ERROR",
|
||||
}
|
||||
log := logger.New(cfg)
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
func TestPingDatabase(t *testing.T) {
|
||||
// Setup database
|
||||
for _, db := range testDBs {
|
||||
|
||||
// Call the Ping method
|
||||
err := db.Ping()
|
||||
|
||||
assert.NoError(t, err, "Database should be reachable")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if err := os.Setenv("IS_TEST_ENV", "true"); err != nil {
|
||||
log.Fatalf("Could not set env variable: %v", err)
|
||||
}
|
||||
|
||||
testDBs = make(map[string]*DB)
|
||||
|
||||
fmt.Println("setup")
|
||||
|
||||
setupPostgresForTest()
|
||||
setupSqliteForTest()
|
||||
|
||||
fmt.Println("running tests")
|
||||
|
||||
//Run tests
|
||||
code := m.Run()
|
||||
|
||||
fmt.Println("teardown")
|
||||
|
||||
for _, d := range testDBs {
|
||||
if err := d.Close(); err != nil {
|
||||
log.Fatalf("Could not close db connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Setenv("IS_TEST_ENV", "false"); err != nil {
|
||||
log.Fatalf("Could not set env variable: %v", err)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
|
@ -93,7 +93,12 @@ func (r *DownloadClientRepo) List(ctx context.Context) ([]domain.DownloadClient,
|
|||
return nil, errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
defer func(rows *sql.Rows) {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
r.log.Error().Err(err).Msg("error closing rows")
|
||||
}
|
||||
}(rows)
|
||||
|
||||
for rows.Next() {
|
||||
var f domain.DownloadClient
|
||||
|
@ -245,11 +250,20 @@ func (r *DownloadClientRepo) Update(ctx context.Context, client domain.DownloadC
|
|||
return nil, errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting rows affected")
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nil, errors.New("no rows updated")
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("download_client.update: %d", client.ID)
|
||||
|
||||
// save to cache
|
||||
|
@ -264,22 +278,37 @@ func (r *DownloadClientRepo) Delete(ctx context.Context, clientID int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
defer tx.Rollback()
|
||||
defer func() {
|
||||
var txErr error
|
||||
if p := recover(); p != nil {
|
||||
txErr = tx.Rollback()
|
||||
if txErr != nil {
|
||||
r.log.Error().Err(txErr).Msg("error rolling back transaction")
|
||||
}
|
||||
r.log.Error().Msgf("something went terribly wrong panic: %v", p)
|
||||
} else if err != nil {
|
||||
txErr = tx.Rollback()
|
||||
if txErr != nil {
|
||||
r.log.Error().Err(txErr).Msg("error rolling back transaction")
|
||||
}
|
||||
} else {
|
||||
// All good, commit
|
||||
txErr = tx.Commit()
|
||||
if txErr != nil {
|
||||
r.log.Error().Err(txErr).Msg("error committing transaction")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := r.delete(ctx, tx, clientID); err != nil {
|
||||
if err = r.delete(ctx, tx, clientID); err != nil {
|
||||
return errors.Wrap(err, "error deleting download client: %d", clientID)
|
||||
}
|
||||
|
||||
if err := r.deleteClientFromAction(ctx, tx, clientID); err != nil {
|
||||
if err = r.deleteClientFromAction(ctx, tx, clientID); err != nil {
|
||||
return errors.Wrap(err, "error deleting download client: %d", clientID)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "error deleting download client: %d", clientID)
|
||||
}
|
||||
|
||||
r.log.Info().Msgf("delete download client: %d", clientID)
|
||||
|
||||
r.log.Debug().Msgf("delete download client: %d", clientID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
331
internal/database/download_client_test.go
Normal file
331
internal/database/download_client_test.go
Normal file
|
@ -0,0 +1,331 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getMockDownloadClient() domain.DownloadClient {
|
||||
return domain.DownloadClient{
|
||||
Name: "qbitorrent",
|
||||
Type: domain.DownloadClientTypeQbittorrent,
|
||||
Enabled: true,
|
||||
Host: "host",
|
||||
Port: 2020,
|
||||
TLS: true,
|
||||
TLSSkipVerify: true,
|
||||
Username: "anime",
|
||||
Password: "anime",
|
||||
Settings: domain.DownloadClientSettings{
|
||||
APIKey: "123",
|
||||
Basic: domain.BasicAuth{
|
||||
Auth: true,
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
},
|
||||
Rules: domain.DownloadClientRules{
|
||||
Enabled: true,
|
||||
MaxActiveDownloads: 10,
|
||||
IgnoreSlowTorrents: false,
|
||||
IgnoreSlowTorrentsCondition: domain.IgnoreSlowTorrentsModeAlways,
|
||||
DownloadSpeedThreshold: 0,
|
||||
UploadSpeedThreshold: 0,
|
||||
},
|
||||
ExternalDownloadClientId: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadClientRepo_List(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewDownloadClientRepo(log, db)
|
||||
mockData := getMockDownloadClient()
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_No_Filters [%s]", dbType), func(t *testing.T) {
|
||||
// Insert mock data
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, clients)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_Empty_Database [%s]", dbType), func(t *testing.T) {
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, clients)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
_, err := repo.List(ctx)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_Data_Integrity [%s]", dbType), func(t *testing.T) {
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(clients))
|
||||
assert.Equal(t, createdClient.Name, clients[0].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_Boundary_Value_For_Port [%s]", dbType), func(t *testing.T) {
|
||||
mockData.Port = 65535
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 65535, clients[0].Port)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_Boolean_Flags_Set_To_False [%s]", dbType), func(t *testing.T) {
|
||||
mockData.Enabled = false
|
||||
mockData.TLS = false
|
||||
mockData.TLSSkipVerify = false
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, clients[0].Enabled)
|
||||
assert.Equal(t, false, clients[0].TLS)
|
||||
assert.Equal(t, false, clients[0].TLSSkipVerify)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds_With_Special_Characters_In_Name [%s]", dbType), func(t *testing.T) {
|
||||
mockData.Name = "Special$Name"
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
clients, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Special$Name", clients[0].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadClientRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewDownloadClientRepo(log, db)
|
||||
mockData := getMockDownloadClient()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
createdClient, _ := repo.Store(context.Background(), mockData)
|
||||
foundClient, err := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, foundClient)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_With_Nonexistent_ID [%s]", dbType), func(t *testing.T) {
|
||||
_, err := repo.FindByID(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "no client configured", err.Error())
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_With_Negative_ID [%s]", dbType), func(t *testing.T) {
|
||||
_, err := repo.FindByID(context.Background(), -1)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
_, err := repo.FindByID(ctx, 1)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_After_Client_Deleted [%s]", dbType), func(t *testing.T) {
|
||||
createdClient, _ := repo.Store(context.Background(), mockData)
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
_, err := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "no client configured", err.Error())
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds_With_Data_Integrity [%s]", dbType), func(t *testing.T) {
|
||||
createdClient, _ := repo.Store(context.Background(), mockData)
|
||||
foundClient, err := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, createdClient.Name, foundClient.Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds_From_Cache [%s]", dbType), func(t *testing.T) {
|
||||
createdClient, _ := repo.Store(context.Background(), mockData)
|
||||
foundClient1, _ := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
foundClient2, err := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, foundClient1, foundClient2)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadClientRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewDownloadClientRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
mockData := getMockDownloadClient()
|
||||
createdClient, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
//TODO: Is this okay? Should we be able to store a client with no name (empty string)?
|
||||
t.Run(fmt.Sprintf("Store_Succeeds?_With_Missing_Required_Fields [%s]", dbType), func(t *testing.T) {
|
||||
badMockData := domain.DownloadClient{
|
||||
Type: "",
|
||||
Enabled: false,
|
||||
Host: "",
|
||||
Port: 0,
|
||||
TLS: false,
|
||||
TLSSkipVerify: false,
|
||||
Username: "",
|
||||
Password: "",
|
||||
Settings: domain.DownloadClientSettings{},
|
||||
}
|
||||
createdClient, err := repo.Store(context.Background(), badMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
mockData := getMockDownloadClient()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
_, err := repo.Store(ctx, mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds_And_Caches [%s]", dbType), func(t *testing.T) {
|
||||
mockData := getMockDownloadClient()
|
||||
createdClient, _ := repo.Store(context.Background(), mockData)
|
||||
|
||||
cachedClient, _ := repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.Equal(t, createdClient, cachedClient)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadClientRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewDownloadClientRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Successfully_Updates_Record [%s]", dbType), func(t *testing.T) {
|
||||
mockClient := getMockDownloadClient()
|
||||
|
||||
createdClient, _ := repo.Store(context.Background(), mockClient)
|
||||
createdClient.Name = "updatedName"
|
||||
updatedClient, err := repo.Update(context.Background(), *createdClient)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "updatedName", updatedClient.Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), updatedClient.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_With_Missing_ID [%s]", dbType), func(t *testing.T) {
|
||||
badMockData := getMockDownloadClient()
|
||||
badMockData.ID = 0
|
||||
|
||||
_, err := repo.Update(context.Background(), badMockData)
|
||||
|
||||
assert.Error(t, err)
|
||||
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_With_Nonexistent_ID [%s]", dbType), func(t *testing.T) {
|
||||
badMockData := getMockDownloadClient()
|
||||
badMockData.ID = 9999
|
||||
|
||||
_, err := repo.Update(context.Background(), badMockData)
|
||||
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_With_Missing_Required_Fields [%s]", dbType), func(t *testing.T) {
|
||||
badMockData := domain.DownloadClient{}
|
||||
|
||||
_, err := repo.Update(context.Background(), badMockData)
|
||||
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadClientRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewDownloadClientRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Successfully_Deletes_Client [%s]", dbType), func(t *testing.T) {
|
||||
mockClient := getMockDownloadClient()
|
||||
createdClient, _ := repo.Store(context.Background(), mockClient)
|
||||
|
||||
err := repo.Delete(context.Background(), createdClient.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify client was deleted
|
||||
_, err = repo.FindByID(context.Background(), int32(createdClient.ID))
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_With_Nonexistent_Client_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.Delete(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
mockClient := getMockDownloadClient()
|
||||
createdClient, _ := repo.Store(context.Background(), mockClient)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := repo.Delete(ctx, createdClient.ID)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -303,11 +303,17 @@ func (r *FeedRepo) Update(ctx context.Context, feed *domain.Feed) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -322,11 +328,17 @@ func (r *FeedRepo) UpdateLastRun(ctx context.Context, feedID int) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -342,11 +354,17 @@ func (r *FeedRepo) UpdateLastRunWithData(ctx context.Context, feedID int, data s
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -363,11 +381,17 @@ func (r *FeedRepo) ToggleEnabled(ctx context.Context, id int, enabled bool) erro
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -381,12 +405,18 @@ func (r *FeedRepo) Delete(ctx context.Context, id int) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
r.log.Info().Msgf("feed.delete: successfully deleted: %v", id)
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("feed.delete: successfully deleted: %v", id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func (r *FeedCacheRepo) Get(feedId int, key string) ([]byte, error) {
|
|||
}
|
||||
|
||||
var value []byte
|
||||
var ttl time.Duration
|
||||
var ttl time.Time
|
||||
|
||||
if err := row.Scan(&value, &ttl); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
|
@ -207,11 +207,17 @@ func (r *FeedCacheRepo) Delete(ctx context.Context, feedId int, key string) erro
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
332
internal/database/feed_cache_test.go
Normal file
332
internal/database/feed_cache_test.go
Normal file
|
@ -0,0 +1,332 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFeedCacheRepo_Get(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
value, err := repo.Get(mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("test_value"), value)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID, "test_key")
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Fails_NoRows [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
value, err := repo.Get(-1, "non_existent_key")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, value)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Fails_Foreign_Key_Constraint [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Put(999, "bad_foreign_key", []byte("test_value"), time.Now().Add(-time.Hour))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Execute
|
||||
value, err := repo.Get(999, "bad_foreign_key")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_GetByFeed(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("GetByFeed_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
items, err := repo.GetByFeed(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, items, 1)
|
||||
assert.Equal(t, "test_key", items[0].Key)
|
||||
assert.Equal(t, []byte("test_value"), items[0].Value)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID, "test_key")
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("GetByFeed_Empty [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
items, err := repo.GetByFeed(context.Background(), -1)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, items)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_Exists(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Exists_True [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
exists, err := repo.Exists(mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exists)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID, "test_key")
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Exists_False [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
exists, err := repo.Exists(-1, "nonexistent_key")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_Put(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Put_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
value, err := repo.Get(mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("test_value"), value)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID, "test_key")
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Put_Fails_InvalidID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Put(-1, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
exists, err := repo.Exists(mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_NoRecord [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Delete(context.Background(), -1, "nonexistent_key")
|
||||
|
||||
// Verify
|
||||
assert.ErrorIs(t, err, domain.ErrRecordNotFound)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_DeleteByFeed(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteByFeed_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.Put(mockData.ID, "test_key", []byte("test_value"), time.Now().Add(time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.DeleteByFeed(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
exists, err := repo.Exists(mockData.ID, "test_key")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteByFeed_Fails_NoRecords [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.DeleteByFeed(context.Background(), -1)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedCacheRepo_DeleteStale(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedCacheRepo(log, db)
|
||||
feedRepo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteStale_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
err = feedRepo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Adding a stale record (older than 30 days)
|
||||
err = repo.Put(mockData.ID, "test_stale_key", []byte("test_stale_value"), time.Now().AddDate(0, 0, -31))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.DeleteStale(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
exists, err := repo.Exists(mockData.ID, "test_stale_key")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
||||
// Cleanup
|
||||
_ = feedRepo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteStale_Fails_NoRecords [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.DeleteStale(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
480
internal/database/feed_test.go
Normal file
480
internal/database/feed_test.go
Normal file
|
@ -0,0 +1,480 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockFeed() *domain.Feed {
|
||||
settings := &domain.FeedSettingsJSON{
|
||||
DownloadType: domain.FeedDownloadTypeTorrent,
|
||||
}
|
||||
|
||||
return &domain.Feed{
|
||||
Name: "ExampleFeed",
|
||||
Type: "RSS",
|
||||
Enabled: true,
|
||||
URL: "https://example.com/feed",
|
||||
Interval: 15,
|
||||
Timeout: 30,
|
||||
ApiKey: "API_KEY_HERE",
|
||||
IndexerID: 1,
|
||||
Settings: settings,
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
|
||||
// Execute
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
feed, err := repo.FindByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, mockData.Name, feed.Name)
|
||||
assert.Equal(t, mockData.Type, feed.Type)
|
||||
assert.Equal(t, mockData.Enabled, feed.Enabled)
|
||||
assert.Equal(t, mockData.URL, feed.URL)
|
||||
assert.Equal(t, mockData.Interval, feed.Interval)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_Missing_Wrong_Foreign_Key [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update data
|
||||
mockData.Name = "NewName"
|
||||
mockData.Type = "NewType"
|
||||
|
||||
// Execute
|
||||
err = repo.Update(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedFeed, err := repo.FindByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "NewName", updatedFeed.Name)
|
||||
assert.Equal(t, "NewType", updatedFeed.Type)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_Non_Existing_Feed [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
nonExistingFeed := getMockFeed()
|
||||
nonExistingFeed.ID = 9999
|
||||
|
||||
// Execute
|
||||
err := repo.Update(context.Background(), nonExistingFeed)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "sql: no rows in result set")
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
_, err = repo.FindByID(context.Background(), mockData.ID)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_Non_Existing_Feed [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Delete(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "sql: no rows in result set")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
feed, err := repo.FindByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, mockData.Name, feed.Name)
|
||||
assert.Equal(t, mockData.Type, feed.Type)
|
||||
assert.Equal(t, mockData.Enabled, feed.Enabled)
|
||||
assert.Equal(t, mockData.URL, feed.URL)
|
||||
assert.Equal(t, mockData.Interval, feed.Interval)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_Wrong_ID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
feed, err := repo.FindByID(context.Background(), -1)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, feed)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_FindByIndexerIdentifier(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFeed()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByIndexerIdentifier_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
mockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
feed, err := repo.FindByIndexerIdentifier(context.Background(), indexer.Identifier)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, feed)
|
||||
assert.Equal(t, mockData.Name, feed.Name)
|
||||
assert.Equal(t, mockData.Type, feed.Type)
|
||||
assert.Equal(t, mockData.Enabled, feed.Enabled)
|
||||
assert.Equal(t, mockData.URL, feed.URL)
|
||||
assert.Equal(t, mockData.Interval, feed.Interval)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByIndexerIdentifier_Fails_Wrong_Identifier [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
feed, err := repo.FindByIndexerIdentifier(context.Background(), "wrong-identifier")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, feed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_Find(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
|
||||
indexerMockData := getMockIndexer()
|
||||
feedMockData1 := getMockFeed()
|
||||
feedMockData2 := getMockFeed()
|
||||
// Change some values in feedMockData2 for variety
|
||||
feedMockData2.Name = "Different Feed"
|
||||
feedMockData2.URL = "http://different.url"
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData1.IndexerID = int(indexer.ID)
|
||||
feedMockData2.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), feedMockData1)
|
||||
assert.NoError(t, err)
|
||||
err = repo.Store(context.Background(), feedMockData2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
feeds, err := repo.Find(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Len(t, feeds, 2)
|
||||
|
||||
// Cleanup
|
||||
for _, feed := range feeds {
|
||||
_ = repo.Delete(context.Background(), feed.ID)
|
||||
}
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Fails_EmptyDB [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
feeds, err := repo.Find(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, feeds)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_GetLastRunDataByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
|
||||
indexerMockData := getMockIndexer()
|
||||
feedMockData := getMockFeed()
|
||||
feedMockData.LastRunData = "Some data"
|
||||
|
||||
t.Run(fmt.Sprintf("GetLastRunDataByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), feedMockData)
|
||||
assert.NoError(t, err)
|
||||
err = repo.UpdateLastRunWithData(context.Background(), feedMockData.ID, feedMockData.LastRunData)
|
||||
assert.NoError(t, err)
|
||||
// Execute
|
||||
data, err := repo.GetLastRunDataByID(context.Background(), feedMockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, "Some data", data)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), feedMockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("GetLastRunDataByID_Fails_InvalidID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
_, err := repo.GetLastRunDataByID(context.Background(), -1)
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("GetLastRunDataByID_Fails_NullData [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData.IndexerID = int(indexer.ID)
|
||||
feedMockData.LastRunData = ""
|
||||
err = repo.Store(context.Background(), feedMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
data, err := repo.GetLastRunDataByID(context.Background(), feedMockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Empty(t, data)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), feedMockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_UpdateLastRun(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
|
||||
indexerMockData := getMockIndexer()
|
||||
feedMockData := getMockFeed()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateLastRun_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), feedMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.UpdateLastRun(context.Background(), feedMockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedFeed, err := repo.Find(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updatedFeed)
|
||||
assert.True(t, updatedFeed[0].LastRun.After(time.Now().Add(-1*time.Minute)))
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), feedMockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateLastRun_Fails_InvalidID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.UpdateLastRun(context.Background(), -1)
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_UpdateLastRunWithData(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
|
||||
indexerMockData := getMockIndexer()
|
||||
feedMockData := getMockFeed()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateLastRunWithData_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), feedMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.UpdateLastRunWithData(context.Background(), feedMockData.ID, "newData")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedFeed, err := repo.Find(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updatedFeed)
|
||||
assert.True(t, updatedFeed[0].LastRun.After(time.Now().Add(-1*time.Minute)))
|
||||
assert.Equal(t, "newData", updatedFeed[0].LastRunData)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), feedMockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateLastRunWithData_Fails_InvalidID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.UpdateLastRunWithData(context.Background(), -1, "data")
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeedRepo_ToggleEnabled(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFeedRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
|
||||
indexerMockData := getMockIndexer()
|
||||
feedMockData := getMockFeed()
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
feedMockData.IndexerID = int(indexer.ID)
|
||||
err = repo.Store(context.Background(), feedMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute & Verify
|
||||
err = repo.ToggleEnabled(context.Background(), feedMockData.ID, false)
|
||||
assert.NoError(t, err)
|
||||
updatedFeed, err := repo.FindByID(context.Background(), feedMockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updatedFeed)
|
||||
assert.False(t, updatedFeed.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), feedMockData.ID)
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Fails_InvalidID [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.ToggleEnabled(context.Background(), -1, true)
|
||||
|
||||
// Verify
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1001,11 +1001,17 @@ func (r *FilterRepo) Update(ctx context.Context, filter *domain.Filter) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1257,11 +1263,17 @@ func (r *FilterRepo) ToggleEnabled(ctx context.Context, filterID int, enabled bo
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1385,12 +1397,18 @@ func (r *FilterRepo) Delete(ctx context.Context, filterID int) error {
|
|||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
result, err := r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
r.log.Info().Msgf("filter.delete: successfully deleted: %v", filterID)
|
||||
if rowsAffected, err := result.RowsAffected(); err != nil {
|
||||
return errors.Wrap(err, "error getting rows affected")
|
||||
} else if rowsAffected == 0 {
|
||||
return domain.ErrRecordNotFound
|
||||
}
|
||||
|
||||
r.log.Debug().Msgf("filter.delete: successfully deleted: %v", filterID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
791
internal/database/filter_test.go
Normal file
791
internal/database/filter_test.go
Normal file
|
@ -0,0 +1,791 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockFilter() *domain.Filter {
|
||||
return &domain.Filter{
|
||||
Name: "New Filter",
|
||||
Enabled: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
MinSize: "10mb",
|
||||
MaxSize: "20mb",
|
||||
Delay: 60,
|
||||
Priority: 1,
|
||||
MaxDownloads: 100,
|
||||
MaxDownloadsUnit: domain.FilterMaxDownloadsHour,
|
||||
MatchReleases: "BRRip",
|
||||
ExceptReleases: "BRRip",
|
||||
UseRegex: false,
|
||||
MatchReleaseGroups: "AMIABLE",
|
||||
ExceptReleaseGroups: "NTb",
|
||||
Scene: false,
|
||||
Origins: nil,
|
||||
ExceptOrigins: nil,
|
||||
Bonus: nil,
|
||||
Freeleech: false,
|
||||
FreeleechPercent: "100%",
|
||||
SmartEpisode: false,
|
||||
Shows: "Is It Wrong to Try to Pick Up Girls in a Dungeon?",
|
||||
Seasons: "4",
|
||||
Episodes: "500",
|
||||
Resolutions: []string{"1080p"},
|
||||
Codecs: []string{"x264"},
|
||||
Sources: []string{"BluRay"},
|
||||
Containers: []string{"mkv"},
|
||||
MatchHDR: []string{"HDR10"},
|
||||
ExceptHDR: []string{"HDR10"},
|
||||
MatchOther: []string{"Atmos"},
|
||||
ExceptOther: []string{"Atmos"},
|
||||
Years: "2023",
|
||||
Artists: "",
|
||||
Albums: "",
|
||||
MatchReleaseTypes: []string{"Remux"},
|
||||
ExceptReleaseTypes: "Remux",
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"Lossless"},
|
||||
Media: []string{"CD"},
|
||||
PerfectFlac: true,
|
||||
Cue: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
MatchCategories: "Anime",
|
||||
ExceptCategories: "Anime",
|
||||
MatchUploaders: "SubsPlease",
|
||||
ExceptUploaders: "SubsPlease",
|
||||
MatchLanguage: []string{"English", "Japanese"},
|
||||
ExceptLanguage: []string{"English", "Japanese"},
|
||||
Tags: "Anime, x264",
|
||||
ExceptTags: "Anime, x264",
|
||||
TagsAny: "Anime, x264",
|
||||
ExceptTagsAny: "Anime, x264",
|
||||
TagsMatchLogic: "AND",
|
||||
ExceptTagsMatchLogic: "AND",
|
||||
MatchReleaseTags: "Anime, x264",
|
||||
ExceptReleaseTags: "Anime, x264",
|
||||
UseRegexReleaseTags: true,
|
||||
MatchDescription: "Anime, x264",
|
||||
ExceptDescription: "Anime, x264",
|
||||
UseRegexDescription: true,
|
||||
}
|
||||
}
|
||||
|
||||
func getMockFilterExternal() domain.FilterExternal {
|
||||
return domain.FilterExternal{
|
||||
Name: "ExternalFilter",
|
||||
Index: 1,
|
||||
Type: domain.ExternalFilterTypeExec,
|
||||
Enabled: true,
|
||||
ExecCmd: "",
|
||||
ExecArgs: "",
|
||||
ExecExpectStatus: 0,
|
||||
WebhookHost: "",
|
||||
WebhookMethod: "",
|
||||
WebhookData: "",
|
||||
WebhookHeaders: "",
|
||||
WebhookExpectStatus: 0,
|
||||
WebhookRetryStatus: "",
|
||||
WebhookRetryAttempts: 0,
|
||||
WebhookRetryDelaySeconds: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
assert.Equal(t, mockData.Name, createdFilters[0].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Missing_or_empty_fields [%s]", dbType), func(t *testing.T) {
|
||||
mockData := domain.Filter{}
|
||||
err := repo.Store(context.Background(), &mockData)
|
||||
assert.Error(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, createdFilters)
|
||||
|
||||
// Cleanup
|
||||
// No cleanup needed
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Fails_With_Context_Timeout [%s]", dbType), func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := repo.Store(ctx, mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update mockData
|
||||
mockData.Name = "Updated Filter"
|
||||
mockData.Enabled = false
|
||||
mockData.CreatedAt = time.Now()
|
||||
|
||||
// Execute
|
||||
err = repo.Update(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
assert.Equal(t, "Updated Filter", createdFilters[0].Name)
|
||||
assert.Equal(t, false, createdFilters[0].Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
mockData.ID = -1
|
||||
err := repo.Update(context.Background(), mockData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
assert.Equal(t, mockData.Name, createdFilters[0].Name)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the filter is deleted
|
||||
filter, err := repo.FindByID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filter)
|
||||
assert.Equal(t, 0, filter.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Fails_No_Record [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.Delete(context.Background(), 9999)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_UpdatePartial(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdatePartial_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
updatedName := "Updated Name"
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
// Execute
|
||||
updateData := domain.FilterUpdate{ID: createdFilters[0].ID, Name: &updatedName}
|
||||
err = repo.UpdatePartial(context.Background(), updateData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the filter is updated
|
||||
filter, err := repo.FindByID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filter)
|
||||
assert.Equal(t, updatedName, filter.Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("UpdatePartial_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
updatedName := "Should Fail"
|
||||
updateData := domain.FilterUpdate{ID: -1, Name: &updatedName}
|
||||
err := repo.UpdatePartial(context.Background(), updateData)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_ToggleEnabled(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
assert.Equal(t, true, createdFilters[0].Enabled)
|
||||
|
||||
// Execute
|
||||
err = repo.ToggleEnabled(context.Background(), mockData.ID, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify that the filter is updated
|
||||
filter, err := repo.FindByID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filter)
|
||||
assert.Equal(t, false, filter.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("ToggleEnabled_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.ToggleEnabled(context.Background(), -1, false)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_ListFilters(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("ListFilters_ReturnsFilters [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
for i := 0; i < 10; i++ {
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Execute
|
||||
filters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
|
||||
// Cleanup
|
||||
for _, filter := range filters {
|
||||
_ = repo.Delete(context.Background(), filter.ID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("ListFilters_ReturnsEmptyList [%s]", dbType), func(t *testing.T) {
|
||||
filters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, filters)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_Find(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Basic [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
mockData.Name = "Test Filter"
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
params := domain.FilterQueryParams{
|
||||
Search: "Test",
|
||||
}
|
||||
|
||||
// Execute
|
||||
filters, err := repo.Find(context.Background(), params)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), filters[0].ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Sort [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
for i := 0; i < 10; i++ {
|
||||
mockData.Name = fmt.Sprintf("Test Filter %d", i)
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
params := domain.FilterQueryParams{
|
||||
Sort: map[string]string{
|
||||
"name": "desc",
|
||||
},
|
||||
}
|
||||
|
||||
// Execute
|
||||
filters, err := repo.Find(context.Background(), params)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
assert.Equal(t, "Test Filter 9", filters[0].Name)
|
||||
assert.Equal(t, 10, len(filters))
|
||||
|
||||
// Cleanup
|
||||
for _, filter := range filters {
|
||||
_ = repo.Delete(context.Background(), filter.ID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Filters [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
mockData.Name = "New Filter With Filters"
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
allFilter, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, allFilter)
|
||||
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, indexer)
|
||||
|
||||
// Store indexer connection
|
||||
err = repo.StoreIndexerConnection(context.Background(), allFilter[0].ID, int(indexer.ID))
|
||||
|
||||
params := domain.FilterQueryParams{
|
||||
Filters: struct{ Indexers []string }{Indexers: []string{"indexer1"}},
|
||||
}
|
||||
|
||||
// Execute
|
||||
filters, err := repo.Find(context.Background(), params)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
assert.Equal(t, "New Filter With Filters", filters[0].Name)
|
||||
assert.Equal(t, 1, len(filters))
|
||||
|
||||
// Cleanup
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), filters[0].ID)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := repo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
// Execute
|
||||
filter, err := repo.FindByID(context.Background(), createdFilters[0].ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filter)
|
||||
assert.Equal(t, createdFilters[0].ID, filter.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), createdFilters[0].ID)
|
||||
})
|
||||
|
||||
// TODO: This should succeed, but it fails because we are not handling the error correctly. Fix this.
|
||||
t.Run(fmt.Sprintf("FindByID_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
// Test using an invalid ID
|
||||
filter, err := repo.FindByID(context.Background(), -1)
|
||||
assert.NoError(t, err) // should return an error
|
||||
assert.NotNil(t, filter) // should be nil
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_FindByIndexerIdentifier(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByIndexerIdentifier_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, indexer)
|
||||
|
||||
err = repo.StoreIndexerConnection(context.Background(), mockData.ID, int(indexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
filters, err := repo.FindByIndexerIdentifier(context.Background(), indexerMockData.Identifier)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
|
||||
// Cleanup
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByIndexerIdentifier_Fails_Invalid_Identifier [%s]", dbType), func(t *testing.T) {
|
||||
filters, err := repo.FindByIndexerIdentifier(context.Background(), "invalid-identifier")
|
||||
assert.NoError(t, err) // should return an error??
|
||||
assert.Nil(t, filters)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_FindExternalFiltersByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
mockDataExternal := getMockFilterExternal()
|
||||
|
||||
t.Run(fmt.Sprintf("FindExternalFiltersByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.StoreFilterExternal(context.Background(), mockData.ID, []domain.FilterExternal{mockDataExternal})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
filters, err := repo.FindExternalFiltersByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, filters)
|
||||
assert.NotEmpty(t, filters)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindExternalFiltersByID_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
filters, err := repo.FindExternalFiltersByID(context.Background(), -1)
|
||||
assert.NoError(t, err) // should return an error??
|
||||
assert.Nil(t, filters)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_StoreIndexerConnection(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreIndexerConnection_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, indexer)
|
||||
|
||||
// Execute
|
||||
err = repo.StoreIndexerConnection(context.Background(), mockData.ID, int(indexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreIndexerConnection_Fails_Invalid_IDs [%s]", dbType), func(t *testing.T) {
|
||||
// Execute with invalid IDs
|
||||
err := repo.StoreIndexerConnection(context.Background(), -1, -1)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_StoreIndexerConnections(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreIndexerConnections_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var indexers []domain.Indexer
|
||||
for i := 0; i < 2; i++ {
|
||||
// identifier must be unique
|
||||
indexerMockData.Identifier = fmt.Sprintf("indexer%d", i)
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
indexers = append(indexers, *indexer)
|
||||
}
|
||||
|
||||
// Execute
|
||||
err = repo.StoreIndexerConnections(context.Background(), mockData.ID, indexers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Validate that the connections were successfully stored in the database
|
||||
connections, err := repo.FindByIndexerIdentifier(context.Background(), indexerMockData.Identifier)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, connections)
|
||||
assert.NotEmpty(t, connections)
|
||||
|
||||
// Cleanup
|
||||
for _, indexer := range indexers {
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
}
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreIndexerConnections_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.StoreIndexerConnections(context.Background(), -1, []domain.Indexer{})
|
||||
assert.NoError(t, err) //TODO: // this should return an error.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_StoreFilterExternal(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
mockDataExternal := getMockFilterExternal()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterExternal_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.StoreFilterExternal(context.Background(), mockData.ID, []domain.FilterExternal{mockDataExternal})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreFilterExternal_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.StoreFilterExternal(context.Background(), -1, []domain.FilterExternal{})
|
||||
assert.NoError(t, err) // TODO: this should return an error
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_DeleteIndexerConnections(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
indexerRepo := NewIndexerRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
indexerMockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteIndexerConnections_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
indexer, err := indexerRepo.Store(context.Background(), indexerMockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, indexer)
|
||||
|
||||
err = repo.StoreIndexerConnection(context.Background(), mockData.ID, int(indexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.DeleteIndexerConnections(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Validate that the connections were successfully deleted from the database
|
||||
connections, err := repo.FindByIndexerIdentifier(context.Background(), indexerMockData.Identifier)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, connections)
|
||||
|
||||
// Cleanup
|
||||
_ = indexerRepo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteIndexerConnections_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.DeleteIndexerConnections(context.Background(), -1)
|
||||
assert.NoError(t, err) // TODO: this should return an error
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_DeleteFilterExternal(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
mockData := getMockFilter()
|
||||
mockDataExternal := getMockFilterExternal()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteFilterExternal_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.StoreFilterExternal(context.Background(), mockData.ID, []domain.FilterExternal{mockDataExternal})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.DeleteFilterExternal(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Validate that the connections were successfully deleted from the database
|
||||
connections, err := repo.FindExternalFiltersByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, connections)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteFilterExternal_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
err := repo.DeleteFilterExternal(context.Background(), -1)
|
||||
assert.NoError(t, err) // TODO: this should return an error
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRepo_GetDownloadsByFilterId(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewFilterRepo(log, db)
|
||||
releaseRepo := NewReleaseRepo(log, db)
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
mockData := getMockFilter()
|
||||
mockRelease := getMockRelease()
|
||||
mockAction := getMockAction()
|
||||
mockReleaseActionStatus := getMockReleaseActionStatus()
|
||||
|
||||
t.Run(fmt.Sprintf("GetDownloadsByFilterId_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
mockAction.FilterID = mockData.ID
|
||||
mockAction.ClientID = int32(createdClient.ID)
|
||||
|
||||
action, err := actionRepo.Store(context.Background(), mockAction)
|
||||
|
||||
mockReleaseActionStatus.FilterID = int64(mockData.ID)
|
||||
mockRelease.FilterID = mockData.ID
|
||||
|
||||
err = releaseRepo.Store(context.Background(), mockRelease)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mockReleaseActionStatus.ActionID = int64(action.ID)
|
||||
mockReleaseActionStatus.ReleaseID = mockRelease.ID
|
||||
|
||||
err = releaseRepo.StoreReleaseActionStatus(context.Background(), mockReleaseActionStatus)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
downloads, err := repo.GetDownloadsByFilterId(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, downloads)
|
||||
assert.Equal(t, downloads, &domain.FilterDownloads{
|
||||
HourCount: 1,
|
||||
DayCount: 1,
|
||||
WeekCount: 1,
|
||||
MonthCount: 1,
|
||||
TotalCount: 1,
|
||||
})
|
||||
|
||||
// Cleanup
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: action.ID})
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
_ = releaseRepo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("GetDownloadsByFilterId_Fails_Invalid_ID [%s]", dbType), func(t *testing.T) {
|
||||
downloads, err := repo.GetDownloadsByFilterId(context.Background(), -1)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, downloads)
|
||||
assert.Equal(t, downloads, &domain.FilterDownloads{
|
||||
HourCount: 0,
|
||||
DayCount: 0,
|
||||
WeekCount: 0,
|
||||
MonthCount: 0,
|
||||
TotalCount: 0,
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
}
|
207
internal/database/indexer_test.go
Normal file
207
internal/database/indexer_test.go
Normal file
|
@ -0,0 +1,207 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockIndexer() domain.Indexer {
|
||||
return domain.Indexer{
|
||||
ID: 0,
|
||||
Name: "indexer1",
|
||||
Identifier: "indexer1",
|
||||
Enabled: true,
|
||||
Implementation: "meh",
|
||||
BaseURL: "ok",
|
||||
Settings: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewIndexerRepo(log, db)
|
||||
mockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdIndexer, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
indexer, err := repo.FindByID(context.Background(), int(createdIndexer.ID))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, mockData.Name, createdIndexer.Name)
|
||||
assert.Equal(t, mockData.Identifier, createdIndexer.Identifier)
|
||||
assert.Equal(t, mockData.Enabled, indexer.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), int(createdIndexer.ID))
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewIndexerRepo(log, db)
|
||||
|
||||
initialData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdIndexer, err := repo.Store(context.Background(), initialData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdIndexer.Name = "UpdatedName"
|
||||
createdIndexer.Enabled = false
|
||||
|
||||
// Execute
|
||||
updatedIndexer, err := repo.Update(context.Background(), *createdIndexer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "UpdatedName", updatedIndexer.Name)
|
||||
assert.Equal(t, createdIndexer.Enabled, updatedIndexer.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), int(updatedIndexer.ID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_List(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewIndexerRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
mockData1 := getMockIndexer()
|
||||
mockData1.Name = "Indexer1"
|
||||
mockData1.Identifier = "Identifier1"
|
||||
|
||||
mockData2 := getMockIndexer()
|
||||
mockData2.Name = "Indexer2"
|
||||
mockData2.Identifier = "Identifier2"
|
||||
|
||||
createdIndexer1, err := repo.Store(context.Background(), mockData1)
|
||||
assert.NoError(t, err)
|
||||
createdIndexer2, err := repo.Store(context.Background(), mockData2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
indexers, err := repo.List(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Contains(t, indexers, *createdIndexer1)
|
||||
assert.Contains(t, indexers, *createdIndexer2)
|
||||
|
||||
assert.Equal(t, 2, len(indexers))
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), int(createdIndexer1.ID))
|
||||
_ = repo.Delete(context.Background(), int(createdIndexer2.ID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
repo := NewIndexerRepo(log, db)
|
||||
mockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
mockData.Name = "TestIndexer"
|
||||
mockData.Identifier = "TestIdentifier"
|
||||
|
||||
createdIndexer, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
foundIndexer, err := repo.FindByID(context.Background(), int(createdIndexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, createdIndexer.ID, foundIndexer.ID)
|
||||
assert.Equal(t, createdIndexer.Name, foundIndexer.Name)
|
||||
assert.Equal(t, createdIndexer.Identifier, foundIndexer.Identifier)
|
||||
assert.Equal(t, createdIndexer.Enabled, foundIndexer.Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), int(createdIndexer.ID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_FindByFilterID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIndexerRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
|
||||
filterMockData := getMockFilter()
|
||||
mockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := filterRepo.Store(context.Background(), filterMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
indexer, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, indexer)
|
||||
|
||||
err = filterRepo.StoreIndexerConnection(context.Background(), filterMockData.ID, int(indexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
foundIndexers, err := repo.FindByFilterID(context.Background(), filterMockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Len(t, foundIndexers, 1)
|
||||
assert.Equal(t, indexer.Name, foundIndexers[0].Name)
|
||||
assert.Equal(t, indexer.Identifier, foundIndexers[0].Identifier)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), int(indexer.ID))
|
||||
_ = filterRepo.Delete(context.Background(), filterMockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIndexerRepo(log, db)
|
||||
mockData := getMockIndexer()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdIndexer, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdIndexer)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), int(createdIndexer.ID))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
_, err = repo.FindByID(context.Background(), int(createdIndexer.ID))
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -466,7 +466,7 @@ func (r *IrcRepo) StoreChannel(ctx context.Context, networkID int64, channel *do
|
|||
Set("enabled", channel.Enabled).
|
||||
Set("detached", channel.Detached).
|
||||
Set("name", channel.Name).
|
||||
Set("pass", pass).
|
||||
Set("password", pass).
|
||||
Where(sq.Eq{"id": channel.ID})
|
||||
|
||||
query, args, err := channelQueryBuilder.ToSql()
|
||||
|
@ -534,7 +534,7 @@ func (r *IrcRepo) UpdateChannel(channel *domain.IrcChannel) error {
|
|||
Set("enabled", channel.Enabled).
|
||||
Set("detached", channel.Detached).
|
||||
Set("name", channel.Name).
|
||||
Set("pass", pass).
|
||||
Set("password", pass).
|
||||
Where(sq.Eq{"id": channel.ID})
|
||||
|
||||
query, args, err := channelQueryBuilder.ToSql()
|
||||
|
|
483
internal/database/irc_test.go
Normal file
483
internal/database/irc_test.go
Normal file
|
@ -0,0 +1,483 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockIrcChannel() domain.IrcChannel {
|
||||
return domain.IrcChannel{
|
||||
ID: 0,
|
||||
Enabled: true,
|
||||
Name: "ab_announcement",
|
||||
Password: "password123",
|
||||
Detached: true,
|
||||
Monitoring: false,
|
||||
}
|
||||
}
|
||||
|
||||
func getMockIrcNetwork() domain.IrcNetwork {
|
||||
connectedSince := time.Now().Add(-time.Hour) // Example time 1 hour ago
|
||||
return domain.IrcNetwork{
|
||||
ID: 0,
|
||||
Name: "Freenode",
|
||||
Enabled: true,
|
||||
Server: "irc.freenode.net",
|
||||
Port: 6667,
|
||||
TLS: true,
|
||||
Pass: "serverpass",
|
||||
Nick: "nickname",
|
||||
Auth: domain.IRCAuth{
|
||||
Mechanism: domain.IRCAuthMechanismSASLPlain,
|
||||
Account: "useraccount",
|
||||
Password: "userpassword",
|
||||
},
|
||||
InviteCommand: "INVITE",
|
||||
UseBouncer: true,
|
||||
BouncerAddr: "bouncer.freenode.net",
|
||||
Channels: []domain.IrcChannel{
|
||||
getMockIrcChannel(),
|
||||
},
|
||||
Connected: true,
|
||||
ConnectedSince: &connectedSince,
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_StoreNetwork(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockData := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreNetwork_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
|
||||
// Execute
|
||||
err := repo.StoreNetwork(context.Background(), &mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEqual(t, int64(0), mockData.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), int64(int(mockData.ID)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_StoreChannel(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
mockChannel := getMockIrcChannel()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreChannel_Insert_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.StoreChannel(context.Background(), mockNetwork.ID, &mockChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEqual(t, int64(0), mockChannel.ID)
|
||||
|
||||
// No need to clean up, since the test below will delete the network
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreChannel_Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreChannel(context.Background(), mockNetwork.ID, &mockChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update mockChannel fields
|
||||
mockChannel.Enabled = false
|
||||
mockChannel.Name = "updated_name"
|
||||
|
||||
// Execute
|
||||
err = repo.StoreChannel(context.Background(), mockNetwork.ID, &mockChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
fetchedChannel, fetchErr := repo.ListChannels(mockNetwork.ID)
|
||||
assert.NoError(t, fetchErr)
|
||||
assert.Equal(t, mockChannel.Enabled, fetchedChannel[0].Enabled)
|
||||
assert.Equal(t, mockChannel.Name, fetchedChannel[0].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_UpdateNetwork(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockData := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateNetwork_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
err := repo.StoreNetwork(context.Background(), &mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, int64(0), mockData.ID)
|
||||
|
||||
// Update mockData fields
|
||||
mockData.Enabled = true
|
||||
mockData.Name = "UpdatedNetworkName"
|
||||
|
||||
// Execute
|
||||
err = repo.UpdateNetwork(context.Background(), &mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedNetwork, fetchErr := repo.GetNetworkByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, fetchErr)
|
||||
assert.Equal(t, mockData.Enabled, updatedNetwork.Enabled)
|
||||
assert.Equal(t, mockData.Name, updatedNetwork.Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_GetNetworkByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockData := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("GetNetworkByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
err := repo.StoreNetwork(context.Background(), &mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, int64(0), mockData.ID)
|
||||
|
||||
// Execute
|
||||
fetchedNetwork, err := repo.GetNetworkByID(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, fetchedNetwork)
|
||||
assert.Equal(t, mockData.ID, fetchedNetwork.ID)
|
||||
assert.Equal(t, mockData.Enabled, fetchedNetwork.Enabled)
|
||||
assert.Equal(t, mockData.Name, fetchedNetwork.Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_DeleteNetwork(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockData := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteNetwork_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
err := repo.StoreNetwork(context.Background(), &mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, int64(0), mockData.ID)
|
||||
|
||||
// Execute
|
||||
err = repo.DeleteNetwork(context.Background(), mockData.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
fetchedNetwork, fetchErr := repo.GetNetworkByID(context.Background(), mockData.ID)
|
||||
assert.Error(t, fetchErr)
|
||||
assert.Nil(t, fetchedNetwork)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_FindActiveNetworks(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
mockData1 := getMockIrcNetwork()
|
||||
mockData1.Enabled = true
|
||||
|
||||
mockData2 := getMockIrcNetwork()
|
||||
mockData2.Enabled = false
|
||||
// These fields are required to be unique
|
||||
mockData2.Server = "irc.example.com"
|
||||
mockData2.Port = 6664
|
||||
mockData2.Nick = "nickname2"
|
||||
|
||||
t.Run(fmt.Sprintf("FindActiveNetworks_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockData1)
|
||||
assert.NoError(t, err)
|
||||
err = repo.StoreNetwork(context.Background(), &mockData2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
activeNetworks, err := repo.FindActiveNetworks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEmpty(t, activeNetworks)
|
||||
assert.Len(t, activeNetworks, 1)
|
||||
assert.True(t, activeNetworks[0].Enabled)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData1.ID)
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData2.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_ListNetworks(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
|
||||
// Prepare mock data
|
||||
mockData1 := getMockIrcNetwork()
|
||||
mockData1.Name = "ZNetwork"
|
||||
mockData2 := getMockIrcNetwork()
|
||||
mockData2.Name = "ANetwork"
|
||||
mockData2.Server = "irc.example.com"
|
||||
mockData2.Port = 6664
|
||||
mockData2.Nick = "nickname2"
|
||||
|
||||
t.Run(fmt.Sprintf("ListNetworks_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockData1)
|
||||
assert.NoError(t, err)
|
||||
err = repo.StoreNetwork(context.Background(), &mockData2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
listedNetworks, err := repo.ListNetworks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEmpty(t, listedNetworks)
|
||||
assert.Len(t, listedNetworks, 2)
|
||||
|
||||
// Verify the order is alphabetical based on the name
|
||||
assert.Equal(t, "ANetwork", listedNetworks[0].Name)
|
||||
assert.Equal(t, "ZNetwork", listedNetworks[1].Name)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData1.ID)
|
||||
_ = repo.DeleteNetwork(context.Background(), mockData2.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_ListChannels(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
mockChannel := getMockIrcChannel()
|
||||
|
||||
t.Run(fmt.Sprintf("ListChannels_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.StoreChannel(context.Background(), mockNetwork.ID, &mockChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
listedChannels, err := repo.ListChannels(mockNetwork.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEmpty(t, listedChannels)
|
||||
assert.Len(t, listedChannels, 1)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_CheckExistingNetwork(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("CheckExistingNetwork_NoMatch [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
existingNetwork, err := repo.CheckExistingNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.Nil(t, existingNetwork)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("CheckExistingNetwork_MatchFound [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
existingNetwork, err := repo.CheckExistingNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, existingNetwork)
|
||||
assert.Equal(t, mockNetwork.Server, existingNetwork.Server)
|
||||
assert.Equal(t, mockNetwork.Port, existingNetwork.Port)
|
||||
assert.Equal(t, mockNetwork.Nick, existingNetwork.Nick)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_StoreNetworkChannels(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
mockChannels := []domain.IrcChannel{getMockIrcChannel()}
|
||||
|
||||
t.Run(fmt.Sprintf("StoreNetworkChannels_DeleteOldChannels [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.StoreNetworkChannels(context.Background(), mockNetwork.ID, mockChannels)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.StoreNetworkChannels(context.Background(), mockNetwork.ID, []domain.IrcChannel{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
existingChannels, err := repo.ListChannels(mockNetwork.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, existingChannels, 0)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("StoreNetworkChannels_InsertNewChannels [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.StoreNetworkChannels(context.Background(), mockNetwork.ID, mockChannels)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
existingChannels, err := repo.ListChannels(mockNetwork.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, existingChannels, len(mockChannels))
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_UpdateChannel(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
mockChannel := getMockIrcChannel()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateChannel_Success [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = repo.StoreChannel(context.Background(), mockNetwork.ID, &mockChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update mockChannel properties
|
||||
updatedChannel := mockChannel
|
||||
updatedChannel.Enabled = false
|
||||
updatedChannel.Name = "updated_name"
|
||||
updatedChannel.Password = "updated_password"
|
||||
|
||||
// Execute
|
||||
err = repo.UpdateChannel(&updatedChannel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
fetchedChannels, err := repo.ListChannels(mockNetwork.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
fetchedChannel := fetchedChannels[0]
|
||||
assert.Equal(t, updatedChannel.Enabled, fetchedChannel.Enabled)
|
||||
assert.Equal(t, updatedChannel.Name, fetchedChannel.Name)
|
||||
assert.Equal(t, updatedChannel.Password, fetchedChannel.Password)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIrcRepo_UpdateInviteCommand(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewIrcRepo(log, db)
|
||||
mockNetwork := getMockIrcNetwork()
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateInviteCommand_Success [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.StoreNetwork(context.Background(), &mockNetwork)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update invite_command
|
||||
newInviteCommand := "/new_invite_command"
|
||||
err = repo.UpdateInviteCommand(mockNetwork.ID, newInviteCommand)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedNetwork, err := repo.ListNetworks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, newInviteCommand, updatedNetwork[0].InviteCommand)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.DeleteNetwork(context.Background(), mockNetwork.ID)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -280,7 +280,7 @@ func (r *NotificationRepo) Delete(ctx context.Context, notificationID int) error
|
|||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
r.log.Info().Msgf("notification.delete: successfully deleted: %v", notificationID)
|
||||
r.log.Debug().Msgf("notification.delete: successfully deleted: %v", notificationID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
236
internal/database/notification_test.go
Normal file
236
internal/database/notification_test.go
Normal file
|
@ -0,0 +1,236 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockNotification() domain.Notification {
|
||||
return domain.Notification{
|
||||
ID: 1,
|
||||
Name: "MockNotification",
|
||||
Type: domain.NotificationTypeSlack,
|
||||
Enabled: true,
|
||||
Events: []string{"event1", "event2"},
|
||||
Token: "mock-token",
|
||||
APIKey: "mock-api-key",
|
||||
Webhook: "https://webhook.example.com",
|
||||
Title: "Mock Title",
|
||||
Icon: "https://icon.example.com",
|
||||
Username: "mock-username",
|
||||
Host: "https://host.example.com",
|
||||
Password: "mock-password",
|
||||
Channel: "#mock-channel",
|
||||
Rooms: "room1,room2",
|
||||
Targets: "target1,target2",
|
||||
Devices: "device1,device2",
|
||||
Priority: 1,
|
||||
Topic: "mock-topic",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
|
||||
mockData := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("Store_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
|
||||
// Execute
|
||||
notification, err := repo.Store(context.Background(), mockData)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, mockData.Name, notification.Name)
|
||||
assert.Equal(t, mockData.Type, notification.Type)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
mockData := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("Update_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Initial setup and Store
|
||||
notification, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, notification)
|
||||
|
||||
// Modify some fields
|
||||
updatedMockData := *notification
|
||||
updatedMockData.Name = "UpdatedName"
|
||||
updatedMockData.Type = domain.NotificationTypeTelegram
|
||||
updatedMockData.Priority = 2
|
||||
|
||||
// Execute Update
|
||||
updatedNotification, err := repo.Update(context.Background(), updatedMockData)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updatedNotification)
|
||||
assert.Equal(t, updatedMockData.Name, updatedNotification.Name)
|
||||
assert.Equal(t, updatedMockData.Type, updatedNotification.Type)
|
||||
assert.Equal(t, updatedMockData.Priority, updatedNotification.Priority)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), updatedNotification.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
mockData := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Initial setup and Store
|
||||
notification, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, notification)
|
||||
|
||||
// Execute Delete
|
||||
err = repo.Delete(context.Background(), notification.ID)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Further verification: Attempt to fetch deleted notification, expect an error or a nil result
|
||||
deletedNotification, err := repo.FindByID(context.Background(), notification.ID)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, deletedNotification)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_Find(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
mockData1 := getMockNotification()
|
||||
mockData2 := getMockNotification()
|
||||
mockData3 := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("Find_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
|
||||
// Clear out any existing notifications
|
||||
notificationsList, _ := repo.List(context.Background())
|
||||
for _, notification := range notificationsList {
|
||||
_ = repo.Delete(context.Background(), notification.ID)
|
||||
}
|
||||
|
||||
_, err := repo.Store(context.Background(), mockData1)
|
||||
assert.NoError(t, err)
|
||||
_, err = repo.Store(context.Background(), mockData2)
|
||||
assert.NoError(t, err)
|
||||
_, err = repo.Store(context.Background(), mockData3)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Setup query params
|
||||
params := domain.NotificationQueryParams{
|
||||
Limit: 2,
|
||||
Offset: 0,
|
||||
}
|
||||
|
||||
// Execute Find
|
||||
notifications, totalCount, err := repo.Find(context.Background(), params)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(notifications)) // TODO: This should be 2 technically since limit is 2, but it's returning 3 because params are not being applied.
|
||||
assert.Equal(t, 3, totalCount)
|
||||
|
||||
// Cleanup
|
||||
notificationsList, _ = repo.List(context.Background())
|
||||
for _, notification := range notificationsList {
|
||||
_ = repo.Delete(context.Background(), notification.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_FindByID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
|
||||
mockData := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
assert.NotNil(t, mockData)
|
||||
notification, err := repo.Store(context.Background(), mockData)
|
||||
|
||||
// Execute
|
||||
notification, err = repo.FindByID(context.Background(), notification.ID)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, notification)
|
||||
assert.Equal(t, mockData.Name, notification.Name)
|
||||
assert.Equal(t, mockData.Type, notification.Type)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), mockData.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepo_List(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewNotificationRepo(log, db)
|
||||
mockData := getMockNotification()
|
||||
|
||||
t.Run(fmt.Sprintf("List_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
notificationsList, _ := repo.List(context.Background())
|
||||
for _, notification := range notificationsList {
|
||||
_ = repo.Delete(context.Background(), notification.ID)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Execute
|
||||
notifications, err := repo.List(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 10, len(notifications))
|
||||
|
||||
// Cleanup
|
||||
for _, notification := range notifications {
|
||||
_ = repo.Delete(context.Background(), notification.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -139,7 +139,7 @@ func (repo *ReleaseRepo) findReleases(ctx context.Context, tx *Tx, params domain
|
|||
if reskey := r.FindAllStringSubmatch(search, -1); len(reskey) != 0 {
|
||||
filter := sq.Or{}
|
||||
for _, found := range reskey {
|
||||
filter = append(filter, ILike(v, strings.ReplaceAll(strings.Trim(strings.Trim(found[1], `"`), `'`), ".", "_")+"%"))
|
||||
filter = append(filter, repo.db.ILike(v, strings.ReplaceAll(strings.Trim(strings.Trim(found[1], `"`), `'`), ".", "_")+"%"))
|
||||
}
|
||||
|
||||
if len(filter) == 0 {
|
||||
|
@ -153,9 +153,9 @@ func (repo *ReleaseRepo) findReleases(ctx context.Context, tx *Tx, params domain
|
|||
|
||||
if len(search) != 0 {
|
||||
if len(whereQueryBuilder) > 1 {
|
||||
whereQueryBuilder = append(whereQueryBuilder, ILike("r.torrent_name", "%"+search+"%"))
|
||||
whereQueryBuilder = append(whereQueryBuilder, repo.db.ILike("r.torrent_name", "%"+search+"%"))
|
||||
} else {
|
||||
whereQueryBuilder = append(whereQueryBuilder, ILike("r.torrent_name", search+"%"))
|
||||
whereQueryBuilder = append(whereQueryBuilder, repo.db.ILike("r.torrent_name", search+"%"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +649,7 @@ func (repo *ReleaseRepo) CanDownloadShow(ctx context.Context, title string, seas
|
|||
queryBuilder := repo.db.squirrel.
|
||||
Select("COUNT(*)").
|
||||
From("release").
|
||||
Where(ILike("title", title+"%"))
|
||||
Where(repo.db.ILike("title", title+"%"))
|
||||
|
||||
if season > 0 && episode > 0 {
|
||||
queryBuilder = queryBuilder.Where(sq.Or{
|
||||
|
|
632
internal/database/release_test.go
Normal file
632
internal/database/release_test.go
Normal file
|
@ -0,0 +1,632 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockRelease() *domain.Release {
|
||||
return &domain.Release{
|
||||
FilterStatus: domain.ReleaseStatusFilterApproved,
|
||||
Rejections: []string{"test", "not-a-match"},
|
||||
Indexer: "BTN",
|
||||
FilterName: "ExampleFilter",
|
||||
Protocol: domain.ReleaseProtocolTorrent,
|
||||
Implementation: domain.ReleaseImplementationIRC,
|
||||
Timestamp: time.Now(),
|
||||
InfoURL: "https://example.com/info",
|
||||
DownloadURL: "https://example.com/download",
|
||||
GroupID: "group123",
|
||||
TorrentID: "torrent123",
|
||||
TorrentName: "Example.Torrent.Name",
|
||||
Size: 123456789,
|
||||
Title: "Example Title",
|
||||
Category: "Movie",
|
||||
Season: 1,
|
||||
Episode: 2,
|
||||
Year: 2023,
|
||||
Resolution: "1080p",
|
||||
Source: "BluRay",
|
||||
Codec: []string{"H.264", "AAC"},
|
||||
Container: "MKV",
|
||||
HDR: []string{"HDR10", "Dolby Vision"},
|
||||
Group: "ExampleGroup",
|
||||
Proper: true,
|
||||
Repack: false,
|
||||
Website: "https://example.com",
|
||||
Type: "Movie",
|
||||
Origin: "P2P",
|
||||
Tags: []string{"Action", "Adventure"},
|
||||
Uploader: "john_doe",
|
||||
PreTime: "10m",
|
||||
FilterID: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func getMockReleaseActionStatus() *domain.ReleaseActionStatus {
|
||||
return &domain.ReleaseActionStatus{
|
||||
ID: 0,
|
||||
Status: domain.ReleasePushStatusApproved,
|
||||
Action: "okay",
|
||||
ActionID: 10,
|
||||
Type: domain.ActionTypeTest,
|
||||
Client: "qbitorrent",
|
||||
Filter: "Test filter",
|
||||
FilterID: 0,
|
||||
Rejections: []string{"one rejection", "two rejections"},
|
||||
ReleaseID: 0,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreReleaseActionStatus_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Execute
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEqual(t, int64(0), mockData.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_StoreReleaseActionStatus(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreReleaseActionStatus_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Execute
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
assert.NotEqual(t, int64(0), releaseActionMockData.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_Find(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
//actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
//releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("FindReleases_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Execute
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Search with query params
|
||||
queryParams := domain.ReleaseQueryParams{
|
||||
Limit: 10,
|
||||
Offset: 0,
|
||||
Sort: map[string]string{
|
||||
"Timestamp": "asc",
|
||||
},
|
||||
Search: "",
|
||||
}
|
||||
|
||||
releases, nextCursor, total, err := repo.Find(context.Background(), queryParams)
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, releases)
|
||||
assert.NotEqual(t, int64(0), total)
|
||||
assert.True(t, nextCursor >= 0)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_FindRecent(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
//actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
//releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("FindRecent_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
// Execute
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releases, err := repo.FindRecent(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, releases)
|
||||
assert.Lenf(t, releases, 1, "Expected 1 release, got %d", len(releases))
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_GetIndexerOptions(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("GetIndexerOptions_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
options, err := repo.GetIndexerOptions(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NotNil(t, options)
|
||||
assert.Len(t, options, 1)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_GetActionStatusByReleaseID(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("GetActionStatusByReleaseID_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
actionStatus, err := repo.GetActionStatus(context.Background(), &domain.GetReleaseActionStatusRequest{Id: int(releaseActionMockData.ID)})
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actionStatus)
|
||||
assert.Equal(t, releaseActionMockData.ID, actionStatus.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_Get(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Get_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
release, err := repo.Get(context.Background(), &domain.GetReleaseRequest{Id: int(mockData.ID)})
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, release)
|
||||
assert.Equal(t, mockData.ID, release.ID)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_Stats(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Stats_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
stats, err := repo.Stats(context.Background())
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, stats)
|
||||
assert.Equal(t, int64(1), stats.PushApprovedCount)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseRepo_CanDownloadShow(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
downloadClientRepo := NewDownloadClientRepo(log, db)
|
||||
filterRepo := NewFilterRepo(log, db)
|
||||
actionRepo := NewActionRepo(log, db, downloadClientRepo)
|
||||
repo := NewReleaseRepo(log, db)
|
||||
|
||||
mockData := getMockRelease()
|
||||
releaseActionMockData := getMockReleaseActionStatus()
|
||||
actionMockData := getMockAction()
|
||||
|
||||
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdClient)
|
||||
|
||||
err = filterRepo.Store(context.Background(), getMockFilter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
createdFilters, err := filterRepo.ListFilters(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdFilters)
|
||||
|
||||
actionMockData.FilterID = createdFilters[0].ID
|
||||
actionMockData.ClientID = int32(createdClient.ID)
|
||||
mockData.FilterID = createdFilters[0].ID
|
||||
|
||||
err = repo.Store(context.Background(), mockData)
|
||||
assert.NoError(t, err)
|
||||
createdAction, err := actionRepo.Store(context.Background(), actionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
releaseActionMockData.ReleaseID = mockData.ID
|
||||
releaseActionMockData.ActionID = int64(createdAction.ID)
|
||||
releaseActionMockData.FilterID = int64(createdFilters[0].ID)
|
||||
|
||||
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
canDownload, err := repo.CanDownloadShow(context.Background(), "Example.Torrent.Name", 1, 2)
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, canDownload)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), &domain.DeleteReleaseRequest{OlderThan: 0})
|
||||
_ = actionRepo.Delete(context.Background(), &domain.DeleteActionRequest{ActionId: createdAction.ID})
|
||||
_ = filterRepo.Delete(context.Background(), createdFilters[0].ID)
|
||||
_ = downloadClientRepo.Delete(context.Background(), createdClient.ID)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ package database
|
|||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/autobrr/autobrr/pkg/errors"
|
||||
|
||||
|
@ -59,6 +60,14 @@ func (db *DB) openSQLite() error {
|
|||
// Enable foreign key checks. For historical reasons, SQLite does not check
|
||||
// foreign key constraints by default. There's some overhead on inserts to
|
||||
// verify foreign key integrity, but it's definitely worth it.
|
||||
|
||||
// Enable it for testing for consistency with postgres.
|
||||
if os.Getenv("IS_TEST_ENV") == "true" {
|
||||
if _, err = db.handler.Exec(`PRAGMA foreign_keys = ON;`); err != nil {
|
||||
return errors.New("foreign keys pragma")
|
||||
}
|
||||
}
|
||||
|
||||
//if _, err = db.handler.Exec(`PRAGMA foreign_keys = ON;`); err != nil {
|
||||
// return errors.New("foreign keys pragma: %w", err)
|
||||
//}
|
||||
|
|
|
@ -122,3 +122,26 @@ func (r *UserRepo) Update(ctx context.Context, user domain.User) error {
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *UserRepo) Delete(ctx context.Context, username string) error {
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Delete("users").
|
||||
Where(sq.Eq{"username": username})
|
||||
|
||||
query, args, err := queryBuilder.ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error building query")
|
||||
}
|
||||
|
||||
// Execute the query.
|
||||
_, err = r.db.handler.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
// Log the deletion.
|
||||
r.log.Debug().Msgf("user.delete: successfully deleted user: %s", username)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
157
internal/database/user_test.go
Normal file
157
internal/database/user_test.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getMockUser() domain.User {
|
||||
return domain.User{
|
||||
ID: 0,
|
||||
Username: "AkenoHimejima",
|
||||
Password: "password",
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepo_Store(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewUserRepo(log, db)
|
||||
|
||||
userMockData := getMockUser()
|
||||
|
||||
t.Run(fmt.Sprintf("StoreUser_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Store(context.Background(), domain.CreateUserRequest{
|
||||
Username: userMockData.Username,
|
||||
Password: userMockData.Password,
|
||||
})
|
||||
|
||||
// Verify
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), userMockData.Username)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepo_Update(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewUserRepo(log, db)
|
||||
|
||||
user := getMockUser()
|
||||
err := repo.Store(context.Background(), domain.CreateUserRequest{
|
||||
Username: user.Username,
|
||||
Password: user.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run(fmt.Sprintf("UpdateUser_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Update the user
|
||||
newPassword := "newPassword123"
|
||||
user.Password = newPassword
|
||||
err := repo.Update(context.Background(), user)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedUser, err := repo.FindByUsername(context.Background(), user.Username)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, newPassword, updatedUser.Password)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), user.Username)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepo_GetUserCount(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewUserRepo(log, db)
|
||||
|
||||
t.Run(fmt.Sprintf("GetUserCount_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
initialCount, err := repo.GetUserCount(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
user := getMockUser()
|
||||
err = repo.Store(context.Background(), domain.CreateUserRequest{
|
||||
Username: user.Username,
|
||||
Password: user.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
updatedCount, err := repo.GetUserCount(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, initialCount+1, updatedCount)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), user.Username)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepo_FindByUsername(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewUserRepo(log, db)
|
||||
|
||||
userMockData := getMockUser()
|
||||
|
||||
t.Run(fmt.Sprintf("FindByUsername_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Execute
|
||||
err := repo.Store(context.Background(), domain.CreateUserRequest{
|
||||
Username: userMockData.Username,
|
||||
Password: userMockData.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
user, err := repo.FindByUsername(context.Background(), userMockData.Username)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, user)
|
||||
assert.Equal(t, userMockData.Username, user.Username)
|
||||
|
||||
// Cleanup
|
||||
_ = repo.Delete(context.Background(), userMockData.Username)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepo_Delete(t *testing.T) {
|
||||
for dbType, db := range testDBs {
|
||||
log := setupLoggerForTest()
|
||||
|
||||
repo := NewUserRepo(log, db)
|
||||
|
||||
user := getMockUser()
|
||||
err := repo.Store(context.Background(), domain.CreateUserRequest{
|
||||
Username: user.Username,
|
||||
Password: user.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run(fmt.Sprintf("DeleteUser_Succeeds [%s]", dbType), func(t *testing.T) {
|
||||
// Setup
|
||||
err := repo.Delete(context.Background(), user.Username)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify
|
||||
_, err = repo.FindByUsername(context.Background(), user.Username)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, domain.ErrRecordNotFound, err)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ type UserRepo interface {
|
|||
FindByUsername(ctx context.Context, username string) (*User, error)
|
||||
Store(ctx context.Context, req CreateUserRequest) error
|
||||
Update(ctx context.Context, user User) error
|
||||
Delete(ctx context.Context, username string) error
|
||||
}
|
||||
|
||||
type User struct {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue