diff --git a/Makefile b/Makefile index 2a0d20f8..2fdb35e6 100644 --- a/Makefile +++ b/Makefile @@ -128,7 +128,11 @@ integration-test: ./miniflux-test >/tmp/miniflux.log 2>&1 & echo "$$!" > "/tmp/miniflux.pid" while ! nc -z localhost 8080; do sleep 1; done - go test -v -tags=integration -count=1 miniflux.app/v2/internal/tests + + TEST_MINIFLUX_BASE_URL=http://127.0.0.1:8080 \ + TEST_MINIFLUX_ADMIN_USERNAME=admin \ + TEST_MINIFLUX_ADMIN_PASSWORD=test123 \ + go test -v -count=1 ./internal/api clean-integration-test: @ kill -9 `cat /tmp/miniflux.pid` diff --git a/client/client.go b/client/client.go index 23ff3d07..1233d47a 100644 --- a/client/client.go +++ b/client/client.go @@ -18,16 +18,44 @@ type Client struct { } // New returns a new Miniflux client. +// Deprecated: use NewClient instead. func New(endpoint string, credentials ...string) *Client { - // Web gives "API Endpoint = https://miniflux.app/v1/", it doesn't work (/v1/v1/me) + return NewClient(endpoint, credentials...) +} + +// NewClient returns a new Miniflux client. +func NewClient(endpoint string, credentials ...string) *Client { + // Trim trailing slashes and /v1 from the endpoint. endpoint = strings.TrimSuffix(endpoint, "/") endpoint = strings.TrimSuffix(endpoint, "/v1") - // trim to https://miniflux.app if len(credentials) == 2 { return &Client{request: &request{endpoint: endpoint, username: credentials[0], password: credentials[1]}} + } else if len(credentials) == 1 { + return &Client{request: &request{endpoint: endpoint, apiKey: credentials[0]}} + } else { + return &Client{request: &request{endpoint: endpoint}} } - return &Client{request: &request{endpoint: endpoint, apiKey: credentials[0]}} +} + +// Healthcheck checks if the application is up and running. +func (c *Client) Healthcheck() error { + body, err := c.request.Get("/healthcheck") + if err != nil { + return fmt.Errorf("miniflux: unable to perform healthcheck: %w", err) + } + defer body.Close() + + responseBodyContent, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("miniflux: unable to read healthcheck response: %w", err) + } + + if string(responseBodyContent) != "OK" { + return fmt.Errorf("miniflux: invalid healthcheck response: %q", responseBodyContent) + } + + return nil } // Version returns the version of the Miniflux instance. @@ -528,6 +556,25 @@ func (c *Client) SaveEntry(entryID int64) error { return err } +// FetchEntryOriginalContent fetches the original content of an entry using the scraper. +func (c *Client) FetchEntryOriginalContent(entryID int64) (string, error) { + body, err := c.request.Get(fmt.Sprintf("/v1/entries/%d/fetch-content", entryID)) + if err != nil { + return "", err + } + defer body.Close() + + var response struct { + Content string `json:"content"` + } + + if err := json.NewDecoder(body).Decode(&response); err != nil { + return "", fmt.Errorf("miniflux: response error (%v)", err) + } + + return response.Content, nil +} + // FetchCounters fetches feed counters. func (c *Client) FetchCounters() (*FeedCounters, error) { body, err := c.request.Get("/v1/feeds/counters") diff --git a/client/doc.go b/client/doc.go index c395793b..48053b59 100644 --- a/client/doc.go +++ b/client/doc.go @@ -12,7 +12,7 @@ This code snippet fetch the list of users: miniflux "miniflux.app/v2/client" ) - client := miniflux.New("https://api.example.org", "admin", "secret") + client := miniflux.NewClient("https://api.example.org", "admin", "secret") users, err := client.Users() if err != nil { fmt.Println(err) diff --git a/client/model.go b/client/model.go index 9d9ab08e..05bc0c2f 100644 --- a/client/model.go +++ b/client/model.go @@ -290,3 +290,7 @@ type VersionResponse struct { Arch string `json:"arch"` OS string `json:"os"` } + +func SetOptionalField[T any](value T) *T { + return &value +} diff --git a/client/request.go b/client/request.go index 1c91316e..45787f4d 100644 --- a/client/request.go +++ b/client/request.go @@ -26,6 +26,7 @@ var ( ErrForbidden = errors.New("miniflux: access forbidden") ErrServerError = errors.New("miniflux: internal server error") ErrNotFound = errors.New("miniflux: resource not found") + ErrBadRequest = errors.New("miniflux: bad request") ) type errorResponse struct { @@ -124,10 +125,10 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser, var resp errorResponse decoder := json.NewDecoder(response.Body) if err := decoder.Decode(&resp); err != nil { - return nil, fmt.Errorf("miniflux: bad request error (%v)", err) + return nil, fmt.Errorf("%w (%v)", ErrBadRequest, err) } - return nil, fmt.Errorf("miniflux: bad request (%s)", resp.ErrorMessage) + return nil, fmt.Errorf("%w (%s)", ErrBadRequest, resp.ErrorMessage) } if response.StatusCode > 400 { diff --git a/internal/api/api_integration_test.go b/internal/api/api_integration_test.go new file mode 100644 index 00000000..141dcf64 --- /dev/null +++ b/internal/api/api_integration_test.go @@ -0,0 +1,2322 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package api // import "miniflux.app/v2/internal/api" + +import ( + "bytes" + "errors" + "fmt" + "io" + "math" + "math/rand" + "os" + "strings" + "testing" + + miniflux "miniflux.app/v2/client" +) + +const skipIntegrationTestsMessage = `Set TEST_MINIFLUX_* environment variables to run the API integration tests` + +type integrationTestConfig struct { + testBaseURL string + testAdminUsername string + testAdminPassword string + testRegularUsername string + testRegularPassword string + testFeedURL string + testFeedTitle string + testSubscriptionTitle string + testWebsiteURL string +} + +func newIntegrationTestConfig() *integrationTestConfig { + getDefaultEnvValues := func(key, defaultValue string) string { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + return value + } + + return &integrationTestConfig{ + testBaseURL: getDefaultEnvValues("TEST_MINIFLUX_BASE_URL", ""), + testAdminUsername: getDefaultEnvValues("TEST_MINIFLUX_ADMIN_USERNAME", ""), + testAdminPassword: getDefaultEnvValues("TEST_MINIFLUX_ADMIN_PASSWORD", ""), + testRegularUsername: getDefaultEnvValues("TEST_MINIFLUX_REGULAR_USERNAME_PREFIX", "regular_test_user"), + testRegularPassword: getDefaultEnvValues("TEST_MINIFLUX_REGULAR_PASSWORD", "regular_test_user_password"), + testFeedURL: getDefaultEnvValues("TEST_MINIFLUX_FEED_URL", "https://miniflux.app/feed.xml"), + testFeedTitle: getDefaultEnvValues("TEST_MINIFLUX_FEED_TITLE", "Miniflux"), + testSubscriptionTitle: getDefaultEnvValues("TEST_MINIFLUX_SUBSCRIPTION_TITLE", "Miniflux Releases"), + testWebsiteURL: getDefaultEnvValues("TEST_MINIFLUX_WEBSITE_URL", "https://miniflux.app"), + } +} + +func (c *integrationTestConfig) isConfigured() bool { + return c.testBaseURL != "" && c.testAdminUsername != "" && c.testAdminPassword != "" && c.testFeedURL != "" && c.testFeedTitle != "" && c.testSubscriptionTitle != "" && c.testWebsiteURL != "" +} + +func (c *integrationTestConfig) genRandomUsername() string { + return fmt.Sprintf("%s_%10d", c.testRegularUsername, rand.Intn(math.MaxInt64)) +} + +func TestIncorrectEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient("incorrect url") + _, err := client.Users() + if err == nil { + t.Fatal(`Using an incorrect URL should raise an error`) + } +} + +func TestHealthcheckEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL) + if err := client.Healthcheck(); err != nil { + t.Fatal(err) + } +} + +func TestVersionEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + version, err := client.Version() + if err != nil { + t.Fatal(err) + } + + if version.Version == "" { + t.Fatal(`Version should not be empty`) + } + + if version.Commit == "" { + t.Fatal(`Commit should not be empty`) + } + + if version.BuildDate == "" { + t.Fatal(`Build date should not be empty`) + } + + if version.GoVersion == "" { + t.Fatal(`Go version should not be empty`) + } + + if version.Compiler == "" { + t.Fatal(`Compiler should not be empty`) + } + + if version.Arch == "" { + t.Fatal(`Arch should not be empty`) + } + + if version.OS == "" { + t.Fatal(`OS should not be empty`) + } +} + +func TestInvalidCredentials(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, "invalid", "invalid") + _, err := client.Users() + if err == nil { + t.Fatal(`Using bad credentials should raise an error`) + } + + if err != miniflux.ErrNotAuthorized { + t.Fatal(`A "Not Authorized" error should be raised`) + } +} + +func TestGetMeEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + user, err := client.Me() + if err != nil { + t.Fatal(err) + } + + if user.Username != testConfig.testAdminUsername { + t.Fatalf(`Invalid username, got %q instead of %q`, user.Username, testConfig.testAdminUsername) + } +} + +func TestGetUsersEndpointAsAdmin(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + users, err := client.Users() + if err != nil { + t.Fatal(err) + } + + if len(users) == 0 { + t.Fatal(`Users should not be empty`) + } + + if users[0].ID == 0 { + t.Fatalf(`Invalid userID, got "%v"`, users[0].ID) + } + + if users[0].Username != testConfig.testAdminUsername { + t.Fatalf(`Invalid username, got "%v" instead of "%v"`, users[0].Username, testConfig.testAdminUsername) + } + + if users[0].Password != "" { + t.Fatalf(`Invalid password, got "%v"`, users[0].Password) + } + + if users[0].Language != "en_US" { + t.Fatalf(`Invalid language, got "%v"`, users[0].Language) + } + + if users[0].Theme != "light_serif" { + t.Fatalf(`Invalid theme, got "%v"`, users[0].Theme) + } + + if users[0].Timezone != "UTC" { + t.Fatalf(`Invalid timezone, got "%v"`, users[0].Timezone) + } + + if !users[0].IsAdmin { + t.Fatalf(`Invalid role, got "%v"`, users[0].IsAdmin) + } + + if users[0].EntriesPerPage != 100 { + t.Fatalf(`Invalid entries per page, got "%v"`, users[0].EntriesPerPage) + } + + if users[0].DisplayMode != "standalone" { + t.Fatalf(`Invalid web app display mode, got "%v"`, users[0].DisplayMode) + } + + if users[0].GestureNav != "tap" { + t.Fatalf(`Invalid gesture navigation, got "%v"`, users[0].GestureNav) + } + + if users[0].DefaultReadingSpeed != 265 { + t.Fatalf(`Invalid default reading speed, got "%v"`, users[0].DefaultReadingSpeed) + } + + if users[0].CJKReadingSpeed != 500 { + t.Fatalf(`Invalid cjk reading speed, got "%v"`, users[0].CJKReadingSpeed) + } +} + +func TestGetUsersEndpointAsRegularUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + _, err = regularUserClient.Users() + if err == nil { + t.Fatal(`Regular users should not have access to the users endpoint`) + } +} + +func TestCreateUserEndpointAsAdmin(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + username := testConfig.genRandomUsername() + regularTestUser, err := client.CreateUser(username, testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer client.DeleteUser(regularTestUser.ID) + + if regularTestUser.Username != username { + t.Fatalf(`Invalid username, got "%v" instead of "%v"`, regularTestUser.Username, username) + } + + if regularTestUser.Password != "" { + t.Fatalf(`Invalid password, got "%v"`, regularTestUser.Password) + } + + if regularTestUser.Language != "en_US" { + t.Fatalf(`Invalid language, got "%v"`, regularTestUser.Language) + } + + if regularTestUser.Theme != "light_serif" { + t.Fatalf(`Invalid theme, got "%v"`, regularTestUser.Theme) + } + + if regularTestUser.Timezone != "UTC" { + t.Fatalf(`Invalid timezone, got "%v"`, regularTestUser.Timezone) + } + + if regularTestUser.IsAdmin { + t.Fatalf(`Invalid role, got "%v"`, regularTestUser.IsAdmin) + } + + if regularTestUser.EntriesPerPage != 100 { + t.Fatalf(`Invalid entries per page, got "%v"`, regularTestUser.EntriesPerPage) + } + + if regularTestUser.DisplayMode != "standalone" { + t.Fatalf(`Invalid web app display mode, got "%v"`, regularTestUser.DisplayMode) + } + + if regularTestUser.GestureNav != "tap" { + t.Fatalf(`Invalid gesture navigation, got "%v"`, regularTestUser.GestureNav) + } + + if regularTestUser.DefaultReadingSpeed != 265 { + t.Fatalf(`Invalid default reading speed, got "%v"`, regularTestUser.DefaultReadingSpeed) + } + + if regularTestUser.CJKReadingSpeed != 500 { + t.Fatalf(`Invalid cjk reading speed, got "%v"`, regularTestUser.CJKReadingSpeed) + } +} + +func TestCreateUserEndpointAsRegularUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + _, err = regularUserClient.CreateUser(regularTestUser.Username, testConfig.testRegularPassword, false) + if err == nil { + t.Fatal(`Regular users should not have access to the create user endpoint`) + } +} + +func TestCannotCreateDuplicateUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.CreateUser(testConfig.testAdminUsername, testConfig.testAdminPassword, true) + if err == nil { + t.Fatal(`Duplicated users should not be allowed`) + } +} + +func TestRemoveUserEndpointAsAdmin(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + user, err := client.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + + if err := client.DeleteUser(user.ID); err != nil { + t.Fatal(err) + } +} + +func TestRemoveUserEndpointAsRegularUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + err = regularUserClient.DeleteUser(regularTestUser.ID) + if err == nil { + t.Fatal(`Regular users should not have access to the remove user endpoint`) + } +} + +func TestGetUserByIDEndpointAsAdmin(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + user, err := client.Me() + if err != nil { + t.Fatal(err) + } + + userByID, err := client.UserByID(user.ID) + if err != nil { + t.Fatal(err) + } + + if userByID.ID != user.ID { + t.Errorf(`Invalid userID, got "%v" instead of "%v"`, userByID.ID, user.ID) + } + + if userByID.Username != user.Username { + t.Errorf(`Invalid username, got "%v" instead of "%v"`, userByID.Username, user.Username) + } + + if userByID.Password != "" { + t.Errorf(`The password field must be empty, got "%v"`, userByID.Password) + } + + if userByID.Language != user.Language { + t.Errorf(`Invalid language, got "%v"`, userByID.Language) + } + + if userByID.Theme != user.Theme { + t.Errorf(`Invalid theme, got "%v"`, userByID.Theme) + } + + if userByID.Timezone != user.Timezone { + t.Errorf(`Invalid timezone, got "%v"`, userByID.Timezone) + } + + if userByID.IsAdmin != user.IsAdmin { + t.Errorf(`Invalid role, got "%v"`, userByID.IsAdmin) + } + + if userByID.EntriesPerPage != user.EntriesPerPage { + t.Errorf(`Invalid entries per page, got "%v"`, userByID.EntriesPerPage) + } + + if userByID.DisplayMode != user.DisplayMode { + t.Errorf(`Invalid web app display mode, got "%v"`, userByID.DisplayMode) + } + + if userByID.GestureNav != user.GestureNav { + t.Errorf(`Invalid gesture navigation, got "%v"`, userByID.GestureNav) + } + + if userByID.DefaultReadingSpeed != user.DefaultReadingSpeed { + t.Errorf(`Invalid default reading speed, got "%v"`, userByID.DefaultReadingSpeed) + } + + if userByID.CJKReadingSpeed != user.CJKReadingSpeed { + t.Errorf(`Invalid cjk reading speed, got "%v"`, userByID.CJKReadingSpeed) + } + + if userByID.EntryDirection != user.EntryDirection { + t.Errorf(`Invalid entry direction, got "%v"`, userByID.EntryDirection) + } + + if userByID.EntryOrder != user.EntryOrder { + t.Errorf(`Invalid entry order, got "%v"`, userByID.EntryOrder) + } +} + +func TestGetUserByIDEndpointAsRegularUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + _, err = regularUserClient.UserByID(regularTestUser.ID) + if err == nil { + t.Fatal(`Regular users should not have access to the user by ID endpoint`) + } +} + +func TestGetUserByUsernameEndpointAsAdmin(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + user, err := client.Me() + if err != nil { + t.Fatal(err) + } + + userByUsername, err := client.UserByUsername(user.Username) + if err != nil { + t.Fatal(err) + } + + if userByUsername.ID != user.ID { + t.Errorf(`Invalid userID, got "%v" instead of "%v"`, userByUsername.ID, user.ID) + } + + if userByUsername.Username != user.Username { + t.Errorf(`Invalid username, got "%v" instead of "%v"`, userByUsername.Username, user.Username) + } + + if userByUsername.Password != "" { + t.Errorf(`The password field must be empty, got "%v"`, userByUsername.Password) + } + + if userByUsername.Language != user.Language { + t.Errorf(`Invalid language, got "%v"`, userByUsername.Language) + } + + if userByUsername.Theme != user.Theme { + t.Errorf(`Invalid theme, got "%v"`, userByUsername.Theme) + } + + if userByUsername.Timezone != user.Timezone { + t.Errorf(`Invalid timezone, got "%v"`, userByUsername.Timezone) + } + + if userByUsername.IsAdmin != user.IsAdmin { + t.Errorf(`Invalid role, got "%v"`, userByUsername.IsAdmin) + } + + if userByUsername.EntriesPerPage != user.EntriesPerPage { + t.Errorf(`Invalid entries per page, got "%v"`, userByUsername.EntriesPerPage) + } + + if userByUsername.DisplayMode != user.DisplayMode { + t.Errorf(`Invalid web app display mode, got "%v"`, userByUsername.DisplayMode) + } + + if userByUsername.GestureNav != user.GestureNav { + t.Errorf(`Invalid gesture navigation, got "%v"`, userByUsername.GestureNav) + } + + if userByUsername.DefaultReadingSpeed != user.DefaultReadingSpeed { + t.Errorf(`Invalid default reading speed, got "%v"`, userByUsername.DefaultReadingSpeed) + } + + if userByUsername.CJKReadingSpeed != user.CJKReadingSpeed { + t.Errorf(`Invalid cjk reading speed, got "%v"`, userByUsername.CJKReadingSpeed) + } + + if userByUsername.EntryDirection != user.EntryDirection { + t.Errorf(`Invalid entry direction, got "%v"`, userByUsername.EntryDirection) + } +} + +func TestGetUserByUsernameEndpointAsRegularUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + _, err = regularUserClient.UserByUsername(regularTestUser.Username) + if err == nil { + t.Fatal(`Regular users should not have access to the user by username endpoint`) + } +} + +func TestUpdateUserEndpointByChangingDefaultTheme(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + userUpdateRequest := &miniflux.UserModificationRequest{ + Theme: miniflux.SetOptionalField("dark_serif"), + } + + updatedUser, err := regularUserClient.UpdateUser(regularTestUser.ID, userUpdateRequest) + if err != nil { + t.Fatal(err) + } + + if updatedUser.Theme != "dark_serif" { + t.Fatalf(`Invalid theme, got "%v"`, updatedUser.Theme) + } +} + +func TestUpdateUserEndpointByChangingDefaultThemeToInvalidValue(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + userUpdateRequest := &miniflux.UserModificationRequest{ + Theme: miniflux.SetOptionalField("invalid_theme"), + } + + _, err = regularUserClient.UpdateUser(regularTestUser.ID, userUpdateRequest) + if err == nil { + t.Fatal(`Updating the user with an invalid theme should raise an error`) + } +} + +func TestRegularUsersCannotUpdateOtherUsers(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + adminUser, err := adminClient.Me() + if err != nil { + t.Fatal(err) + } + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + userUpdateRequest := &miniflux.UserModificationRequest{ + Theme: miniflux.SetOptionalField("dark_serif"), + } + + _, err = regularUserClient.UpdateUser(adminUser.ID, userUpdateRequest) + if err == nil { + t.Fatal(`Regular users should not be able to update other users`) + } +} + +func TestMarkUserAsReadEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.MarkAllAsRead(regularTestUser.ID); err != nil { + t.Fatal(err) + } + + results, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatal(err) + } + + for _, entry := range results.Entries { + if entry.Status != miniflux.EntryStatusRead { + t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) + } + } +} + +func TestCannotMarkUserAsReadAsOtherUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + adminUser, err := adminClient.Me() + if err != nil { + t.Fatal(err) + } + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + if err := regularUserClient.MarkAllAsRead(adminUser.ID); err == nil { + t.Fatalf(`Non-admin users should not be able to mark another user as read`) + } +} + +func TestCreateCategoryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + categoryName := "My category" + category, err := regularUserClient.CreateCategory(categoryName) + if err != nil { + t.Fatal(err) + } + + if category.ID == 0 { + t.Errorf(`Invalid categoryID, got "%v"`, category.ID) + } + + if category.UserID <= 0 { + t.Errorf(`Invalid userID, got "%v"`, category.UserID) + } + + if category.Title != categoryName { + t.Errorf(`Invalid title, got "%v" instead of "%v"`, category.Title, categoryName) + } +} + +func TestCreateCategoryWithEmptyTitle(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.CreateCategory("") + if err == nil { + t.Fatalf(`Creating a category with an empty title should raise an error`) + } +} + +func TestCannotCreateDuplicatedCategory(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + categoryName := "My category" + + if _, err := regularUserClient.CreateCategory(categoryName); err != nil { + t.Fatal(err) + } + + if _, err = regularUserClient.CreateCategory(categoryName); err == nil { + t.Fatalf(`Duplicated categories should not be allowed`) + } +} + +func TestUpdateCatgoryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + categoryName := "My category" + category, err := regularUserClient.CreateCategory(categoryName) + if err != nil { + t.Fatal(err) + } + + updatedCategory, err := regularUserClient.UpdateCategory(category.ID, "new title") + if err != nil { + t.Fatal(err) + } + + if updatedCategory.ID != category.ID { + t.Errorf(`Invalid categoryID, got "%v"`, updatedCategory.ID) + } + + if updatedCategory.UserID != regularTestUser.ID { + t.Errorf(`Invalid userID, got "%v"`, updatedCategory.UserID) + } + + if updatedCategory.Title != "new title" { + t.Errorf(`Invalid title, got "%v" instead of "%v"`, updatedCategory.Title, "new title") + } +} + +func TestUpdateInexistingCategory(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.UpdateCategory(123456789, "new title") + if err == nil { + t.Fatalf(`Updating an inexisting category should raise an error`) + } +} +func TestDeleteCategoryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + categoryName := "My category" + category, err := regularUserClient.CreateCategory(categoryName) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.DeleteCategory(category.ID); err != nil { + t.Fatal(err) + } +} + +func TestCannotDeleteInexistingCategory(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + err := client.DeleteCategory(123456789) + if err == nil { + t.Fatalf(`Deleting an inexisting category should raise an error`) + } +} + +func TestCannotDeleteCategoryOfAnotherUser(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + err = adminClient.DeleteCategory(category.ID) + if err == nil { + t.Fatalf(`Regular users should not be able to delete categories of other users`) + } +} + +func TestGetCategoriesEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + categories, err := regularUserClient.Categories() + if err != nil { + t.Fatal(err) + } + + if len(categories) != 2 { + t.Fatalf(`Invalid number of categories, got %d instead of %d`, len(categories), 1) + } + + if categories[0].UserID != regularTestUser.ID { + t.Fatalf(`Invalid userID, got %d`, categories[0].UserID) + } + + if categories[0].Title != "All" { + t.Fatalf(`Invalid title, got %q instead of %q`, categories[0].Title, "All") + } + + if categories[1].ID != category.ID { + t.Fatalf(`Invalid categoryID, got %d`, categories[0].ID) + } + + if categories[1].UserID != regularTestUser.ID { + t.Fatalf(`Invalid userID, got %d`, categories[0].UserID) + } + + if categories[1].Title != "My category" { + t.Fatalf(`Invalid title, got %q instead of %q`, categories[0].Title, "My category") + } +} + +func TestMarkCategoryAsReadEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + CategoryID: category.ID, + }) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.MarkCategoryAsRead(category.ID); err != nil { + t.Fatal(err) + } + + results, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatal(err) + } + + for _, entry := range results.Entries { + if entry.Status != miniflux.EntryStatusRead { + t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) + } + } +} + +func TestCreateFeedEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + CategoryID: category.ID, + }) + if err != nil { + t.Fatal(err) + } + + if feedID == 0 { + t.Errorf(`Invalid feedID, got "%v"`, feedID) + } +} + +func TestCannotCreateDuplicatedFeed(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + if feedID == 0 { + t.Fatalf(`Invalid feedID, got "%v"`, feedID) + } + + _, err = regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err == nil { + t.Fatalf(`Duplicated feeds should not be allowed`) + } +} + +func TestCreateFeedWithInexistingCategory(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + _, err = regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + CategoryID: 123456789, + }) + + if err == nil { + t.Fatalf(`Creating a feed with an inexisting category should raise an error`) + } +} + +func TestCreateFeedWithEmptyFeedURL(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: "", + }) + if err == nil { + t.Fatalf(`Creating a feed with an empty feed URL should raise an error`) + } +} + +func TestCreateFeedWithInvalidFeedURL(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: "invalid_feed_url", + }) + if err == nil { + t.Fatalf(`Creating a feed with an invalid feed URL should raise an error`) + } +} + +func TestCreateDisabledFeed(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + Disabled: true, + }) + if err != nil { + t.Fatal(err) + } + + feed, err := regularUserClient.Feed(feedID) + if err != nil { + t.Fatal(err) + } + + if !feed.Disabled { + t.Fatalf(`The feed should be disabled`) + } +} + +func TestCreateFeedWithDisabledHTTPCache(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + IgnoreHTTPCache: true, + }) + if err != nil { + t.Fatal(err) + } + + feed, err := regularUserClient.Feed(feedID) + if err != nil { + t.Fatal(err) + } + + if !feed.IgnoreHTTPCache { + t.Fatalf(`The feed should ignore the HTTP cache`) + } +} + +func TestCreateFeedWithScraperRule(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + ScraperRules: "article", + }) + if err != nil { + t.Fatal(err) + } + + feed, err := regularUserClient.Feed(feedID) + if err != nil { + t.Fatal(err) + } + + if feed.ScraperRules != "article" { + t.Fatalf(`The feed should have the scraper rules set to "article"`) + } +} + +func TestUpdateFeedEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + feedUpdateRequest := &miniflux.FeedModificationRequest{ + FeedURL: miniflux.SetOptionalField("https://example.org/feed.xml"), + } + + updatedFeed, err := regularUserClient.UpdateFeed(feedID, feedUpdateRequest) + if err != nil { + t.Fatal(err) + } + + if updatedFeed.FeedURL != "https://example.org/feed.xml" { + t.Fatalf(`Invalid feed URL, got "%v"`, updatedFeed.FeedURL) + } +} + +func TestCannotHaveDuplicateFeedWhenUpdatingFeed(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + if _, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{FeedURL: testConfig.testFeedURL}); err != nil { + t.Fatal(err) + } + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: "https://github.com/miniflux/v2/commits.atom", + }) + if err != nil { + t.Fatal(err) + } + + feedUpdateRequest := &miniflux.FeedModificationRequest{ + FeedURL: miniflux.SetOptionalField(testConfig.testFeedURL), + } + + if _, err := regularUserClient.UpdateFeed(feedID, feedUpdateRequest); err == nil { + t.Fatalf(`Duplicated feeds should not be allowed`) + } +} + +func TestUpdateFeedWithInvalidCategory(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + feedUpdateRequest := &miniflux.FeedModificationRequest{ + CategoryID: miniflux.SetOptionalField(int64(123456789)), + } + + if _, err := regularUserClient.UpdateFeed(feedID, feedUpdateRequest); err == nil { + t.Fatalf(`Updating a feed with an inexisting category should raise an error`) + } +} + +func TestMarkFeedAsReadEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.MarkFeedAsRead(feedID); err != nil { + t.Fatal(err) + } + + results, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatalf(`Failed to get updated entries: %v`, err) + } + + for _, entry := range results.Entries { + if entry.Status != miniflux.EntryStatusRead { + t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) + } + } +} + +func TestFetchCountersEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + counters, err := regularUserClient.FetchCounters() + if err != nil { + t.Fatal(err) + } + + if value, ok := counters.ReadCounters[feedID]; ok && value != 0 { + t.Errorf(`Invalid read counter, got %d`, value) + } + + if value, ok := counters.UnreadCounters[feedID]; !ok || value == 0 { + t.Errorf(`Invalid unread counter, got %d`, value) + } +} + +func TestDeleteFeedEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.DeleteFeed(feedID); err != nil { + t.Fatal(err) + } +} + +func TestRefreshAllFeedsEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + if err := regularUserClient.RefreshAllFeeds(); err != nil { + t.Fatal(err) + } +} + +func TestRefreshFeedEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + if err := regularUserClient.RefreshFeed(feedID); err != nil { + t.Fatal(err) + } +} + +func TestGetFeedEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + feed, err := regularUserClient.Feed(feedID) + if err != nil { + t.Fatal(err) + } + + if feed.ID != feedID { + t.Fatalf(`Invalid feedID, got %d`, feed.ID) + } + + if feed.FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, feed.FeedURL) + } + + if feed.SiteURL != testConfig.testWebsiteURL { + t.Fatalf(`Invalid site URL, got %q`, feed.SiteURL) + } + + if feed.Title != testConfig.testFeedTitle { + t.Fatalf(`Invalid title, got %q`, feed.Title) + } +} + +func TestGetFeedIcon(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + icon, err := regularUserClient.FeedIcon(feedID) + if err != nil { + t.Fatal(err) + } + + if icon == nil { + t.Fatalf(`Invalid icon, got nil`) + } + + if icon.MimeType == "" { + t.Fatalf(`Invalid mime type, got %q`, icon.MimeType) + } + + if len(icon.Data) == 0 { + t.Fatalf(`Invalid data, got empty`) + } + + icon, err = regularUserClient.Icon(icon.ID) + if err != nil { + t.Fatal(err) + } + + if icon == nil { + t.Fatalf(`Invalid icon, got nil`) + } + + if icon.MimeType == "" { + t.Fatalf(`Invalid mime type, got %q`, icon.MimeType) + } + + if len(icon.Data) == 0 { + t.Fatalf(`Invalid data, got empty`) + } +} + +func TestGetFeedIconWithInexistingFeedID(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.FeedIcon(123456789) + if err == nil { + t.Fatalf(`Fetching the icon of an inexisting feed should raise an error`) + } +} + +func TestGetFeedsEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + feeds, err := regularUserClient.Feeds() + if err != nil { + t.Fatal(err) + } + + if len(feeds) != 1 { + t.Fatalf(`Invalid number of feeds, got %d`, len(feeds)) + } + + if feeds[0].ID != feedID { + t.Fatalf(`Invalid feedID, got %d`, feeds[0].ID) + } + + if feeds[0].FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, feeds[0].FeedURL) + } +} + +func TestGetCategoryFeedsEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + CategoryID: category.ID, + }) + if err != nil { + t.Fatal(err) + } + + feeds, err := regularUserClient.CategoryFeeds(category.ID) + if err != nil { + t.Fatal(err) + } + + if len(feeds) != 1 { + t.Fatalf(`Invalid number of feeds, got %d`, len(feeds)) + } + + if feeds[0].ID != feedID { + t.Fatalf(`Invalid feedID, got %d`, feeds[0].ID) + } + + if feeds[0].FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, feeds[0].FeedURL) + } +} + +func TestExportEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + if _, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{FeedURL: testConfig.testFeedURL}); err != nil { + t.Fatal(err) + } + + exportedData, err := regularUserClient.Export() + if err != nil { + t.Fatal(err) + } + + if len(exportedData) == 0 { + t.Fatalf(`Invalid exported data, got empty`) + } + + if !strings.HasPrefix(string(exportedData), " + + + + + + + ` + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + bytesReader := bytes.NewReader([]byte(data)) + if err := regularUserClient.Import(io.NopCloser(bytesReader)); err != nil { + t.Fatal(err) + } +} + +func TestDiscoverSubscriptionsEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + subscriptions, err := client.Discover(testConfig.testWebsiteURL) + if err != nil { + t.Fatal(err) + } + + if len(subscriptions) == 0 { + t.Fatalf(`Invalid number of subscriptions, got %d`, len(subscriptions)) + } + + if subscriptions[0].Title != testConfig.testSubscriptionTitle { + t.Fatalf(`Invalid title, got %q`, subscriptions[0].Title) + } + + if subscriptions[0].URL != testConfig.testFeedURL { + t.Fatalf(`Invalid URL, got %q`, subscriptions[0].URL) + } +} + +func TestDiscoverSubscriptionsWithInvalidURL(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + _, err := client.Discover("invalid_url") + if err == nil { + t.Fatalf(`Discovering subscriptions with an invalid URL should raise an error`) + } +} + +func TestDiscoverSubscriptionsWithNoSubscription(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + client := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + if _, err := client.Discover(testConfig.testBaseURL); err != miniflux.ErrNotFound { + t.Fatalf(`Discovering subscriptions with no subscription should raise a 404 error`) + } +} + +func TestGetAllFeedEntriesEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + results, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatal(err) + } + + if len(results.Entries) == 0 { + t.Fatalf(`Invalid number of entries, got %d`, len(results.Entries)) + } + + if results.Total == 0 { + t.Fatalf(`Invalid total, got %d`, results.Total) + } + + if results.Entries[0].FeedID != feedID { + t.Fatalf(`Invalid feedID, got %d`, results.Entries[0].FeedID) + } + + if results.Entries[0].Feed.FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, results.Entries[0].Feed.FeedURL) + } + + if results.Entries[0].Title == "" { + t.Fatalf(`Invalid title, got empty`) + } +} + +func TestGetAllCategoryEntriesEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + category, err := regularUserClient.CreateCategory("My category") + if err != nil { + t.Fatal(err) + } + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + CategoryID: category.ID, + }) + if err != nil { + t.Fatal(err) + } + + results, err := regularUserClient.CategoryEntries(category.ID, nil) + if err != nil { + t.Fatal(err) + } + + if len(results.Entries) == 0 { + t.Fatalf(`Invalid number of entries, got %d`, len(results.Entries)) + } + + if results.Total == 0 { + t.Fatalf(`Invalid total, got %d`, results.Total) + } + + if results.Entries[0].FeedID != feedID { + t.Fatalf(`Invalid feedID, got %d`, results.Entries[0].FeedID) + } + + if results.Entries[0].Feed.FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, results.Entries[0].Feed.FeedURL) + } + + if results.Entries[0].Title == "" { + t.Fatalf(`Invalid title, got empty`) + } +} + +func TestGetAllEntriesEndpointWithFilter(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + feedEntries, err := regularUserClient.Entries(&miniflux.Filter{FeedID: feedID}) + if err != nil { + t.Fatal(err) + } + + if len(feedEntries.Entries) == 0 { + t.Fatalf(`Invalid number of entries, got %d`, len(feedEntries.Entries)) + } + + if feedEntries.Total == 0 { + t.Fatalf(`Invalid total, got %d`, feedEntries.Total) + } + + if feedEntries.Entries[0].FeedID != feedID { + t.Fatalf(`Invalid feedID, got %d`, feedEntries.Entries[0].FeedID) + } + + if feedEntries.Entries[0].Feed.FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, feedEntries.Entries[0].Feed.FeedURL) + } + + if feedEntries.Entries[0].Title == "" { + t.Fatalf(`Invalid title, got empty`) + } + + recentEntries, err := regularUserClient.Entries(&miniflux.Filter{Order: "published_at", Direction: "desc"}) + if err != nil { + t.Fatal(err) + } + + if len(recentEntries.Entries) == 0 { + t.Fatalf(`Invalid number of entries, got %d`, len(recentEntries.Entries)) + } + + if recentEntries.Total == 0 { + t.Fatalf(`Invalid total, got %d`, recentEntries.Total) + } + + if feedEntries.Entries[0].Title == recentEntries.Entries[0].Title { + t.Fatalf(`Invalid order, got the same title`) + } + + searchedEntries, err := regularUserClient.Entries(&miniflux.Filter{Search: "2.0.8"}) + if err != nil { + t.Fatal(err) + } + + if searchedEntries.Total != 1 { + t.Fatalf(`Invalid total, got %d`, searchedEntries.Total) + } + + if _, err := regularUserClient.Entries(&miniflux.Filter{Status: "invalid"}); err == nil { + t.Fatal(`Using invalid status should raise an error`) + } + + if _, err = regularUserClient.Entries(&miniflux.Filter{Direction: "invalid"}); err == nil { + t.Fatal(`Using invalid direction should raise an error`) + } + + if _, err = regularUserClient.Entries(&miniflux.Filter{Order: "invalid"}); err == nil { + t.Fatal(`Using invalid order should raise an error`) + } +} + +func TestGetEntryEndpoints(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + entry, err := regularUserClient.FeedEntry(feedID, result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if entry.ID != result.Entries[0].ID { + t.Fatalf(`Invalid entryID, got %d`, entry.ID) + } + + if entry.FeedID != feedID { + t.Fatalf(`Invalid feedID, got %d`, entry.FeedID) + } + + if entry.Feed.FeedURL != testConfig.testFeedURL { + t.Fatalf(`Invalid feed URL, got %q`, entry.Feed.FeedURL) + } + + entry, err = regularUserClient.Entry(result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if entry.ID != result.Entries[0].ID { + t.Fatalf(`Invalid entryID, got %d`, entry.ID) + } + + entry, err = regularUserClient.CategoryEntry(result.Entries[0].Feed.Category.ID, result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if entry.ID != result.Entries[0].ID { + t.Fatalf(`Invalid entryID, got %d`, entry.ID) + } +} + +func TestUpdateEntryStatusEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + if err := regularUserClient.UpdateEntries([]int64{result.Entries[0].ID}, miniflux.EntryStatusRead); err != nil { + t.Fatal(err) + } + + entry, err := regularUserClient.Entry(result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if entry.Status != miniflux.EntryStatusRead { + t.Fatalf(`Invalid status, got %q`, entry.Status) + } +} + +func TestUpdateEntryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, nil) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + entryUpdateRequest := &miniflux.EntryModificationRequest{ + Title: miniflux.SetOptionalField("New title"), + Content: miniflux.SetOptionalField("New content"), + } + + updatedEntry, err := regularUserClient.UpdateEntry(result.Entries[0].ID, entryUpdateRequest) + if err != nil { + t.Fatal(err) + } + + if updatedEntry.Title != "New title" { + t.Errorf(`Invalid title, got %q`, updatedEntry.Title) + } + + if updatedEntry.Content != "New content" { + t.Errorf(`Invalid content, got %q`, updatedEntry.Content) + } + + entry, err := regularUserClient.Entry(result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if entry.Title != "New title" { + t.Errorf(`Invalid title, got %q`, entry.Title) + } + + if entry.Content != "New content" { + t.Errorf(`Invalid content, got %q`, entry.Content) + } +} + +func TestToggleBookmarkEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, &miniflux.Filter{Limit: 1}) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + if err := regularUserClient.ToggleBookmark(result.Entries[0].ID); err != nil { + t.Fatal(err) + } + + entry, err := regularUserClient.Entry(result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if !entry.Starred { + t.Fatalf(`The entry should be bookmarked`) + } +} + +func TestSaveEntryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, &miniflux.Filter{Limit: 1}) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + if err := regularUserClient.SaveEntry(result.Entries[0].ID); !errors.Is(err, miniflux.ErrBadRequest) { + t.Fatalf(`Saving an entry should raise a bad request error because no integration is configured`) + } +} + +func TestFetchContentEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, &miniflux.Filter{Limit: 1}) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + content, err := regularUserClient.FetchEntryOriginalContent(result.Entries[0].ID) + if err != nil { + t.Fatal(err) + } + + if content == "" { + t.Fatalf(`Invalid content, got empty`) + } +} + +func TestFlushHistoryEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + feedID, err := regularUserClient.CreateFeed(&miniflux.FeedCreationRequest{ + FeedURL: testConfig.testFeedURL, + }) + if err != nil { + t.Fatal(err) + } + + result, err := regularUserClient.FeedEntries(feedID, &miniflux.Filter{Limit: 3}) + if err != nil { + t.Fatalf(`Failed to get entries: %v`, err) + } + + if err := regularUserClient.UpdateEntries([]int64{result.Entries[0].ID, result.Entries[1].ID}, miniflux.EntryStatusRead); err != nil { + t.Fatal(err) + } + + if err := regularUserClient.FlushHistory(); err != nil { + t.Fatal(err) + } + + readEntries, err := regularUserClient.Entries(&miniflux.Filter{Status: miniflux.EntryStatusRead}) + if err != nil { + t.Fatal(err) + } + + if readEntries.Total != 0 { + t.Fatalf(`Invalid total, got %d`, readEntries.Total) + } + + removedEntries, err := regularUserClient.Entries(&miniflux.Filter{Status: miniflux.EntryStatusRemoved}) + if err != nil { + t.Fatal(err) + } + + if removedEntries.Total != 2 { + t.Fatalf(`Invalid total, got %d`, removedEntries.Total) + } +} diff --git a/internal/tests/category_test.go b/internal/tests/category_test.go deleted file mode 100644 index c5b7b389..00000000 --- a/internal/tests/category_test.go +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestCreateCategory(t *testing.T) { - categoryName := "My category" - client := createClient(t) - category, err := client.CreateCategory(categoryName) - if err != nil { - t.Fatal(err) - } - - if category.ID == 0 { - t.Fatalf(`Invalid categoryID, got "%v"`, category.ID) - } - - if category.UserID <= 0 { - t.Fatalf(`Invalid userID, got "%v"`, category.UserID) - } - - if category.Title != categoryName { - t.Fatalf(`Invalid title, got "%v" instead of "%v"`, category.Title, categoryName) - } -} - -func TestCreateCategoryWithEmptyTitle(t *testing.T) { - client := createClient(t) - _, err := client.CreateCategory("") - if err == nil { - t.Fatal(`The category title should be mandatory`) - } -} - -func TestCannotCreateDuplicatedCategory(t *testing.T) { - client := createClient(t) - - categoryName := "My category" - _, err := client.CreateCategory(categoryName) - if err != nil { - t.Fatal(err) - } - - _, err = client.CreateCategory(categoryName) - if err == nil { - t.Fatal(`Duplicated categories should not be allowed`) - } -} - -func TestUpdateCategory(t *testing.T) { - categoryName := "My category" - client := createClient(t) - category, err := client.CreateCategory(categoryName) - if err != nil { - t.Fatal(err) - } - - categoryName = "Updated category" - category, err = client.UpdateCategory(category.ID, categoryName) - if err != nil { - t.Fatal(err) - } - - if category.ID == 0 { - t.Fatalf(`Invalid categoryID, got "%v"`, category.ID) - } - - if category.UserID <= 0 { - t.Fatalf(`Invalid userID, got "%v"`, category.UserID) - } - - if category.Title != categoryName { - t.Fatalf(`Invalid title, got %q instead of %q`, category.Title, categoryName) - } -} - -func TestUpdateInexistingCategory(t *testing.T) { - client := createClient(t) - - _, err := client.UpdateCategory(4200000, "Test") - if err != miniflux.ErrNotFound { - t.Errorf(`Updating an inexisting category should returns a 404 instead of %v`, err) - } -} - -func TestMarkCategoryAsRead(t *testing.T) { - client := createClient(t) - - feed, category := createFeed(t, client) - - results, err := client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get entries: %v`, err) - } - if results.Total == 0 { - t.Fatalf(`Invalid number of entries: %d`, results.Total) - } - if results.Entries[0].Status != miniflux.EntryStatusUnread { - t.Fatalf(`Invalid entry status, got %q instead of %q`, results.Entries[0].Status, miniflux.EntryStatusUnread) - } - - if err := client.MarkCategoryAsRead(category.ID); err != nil { - t.Fatalf(`Failed to mark category as read: %v`, err) - } - - results, err = client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get updated entries: %v`, err) - } - - for _, entry := range results.Entries { - if entry.Status != miniflux.EntryStatusRead { - t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) - } - } -} - -func TestListCategories(t *testing.T) { - categoryName := "My category" - client := createClient(t) - - _, err := client.CreateCategory(categoryName) - if err != nil { - t.Fatal(err) - } - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - if len(categories) != 2 { - t.Fatalf(`Invalid number of categories, got "%v" instead of "%v"`, len(categories), 2) - } - - if categories[0].ID == 0 { - t.Fatalf(`Invalid categoryID, got "%v"`, categories[0].ID) - } - - if categories[0].UserID <= 0 { - t.Fatalf(`Invalid userID, got "%v"`, categories[0].UserID) - } - - if categories[0].Title != "All" { - t.Fatalf(`Invalid title, got "%v" instead of "%v"`, categories[0].Title, "All") - } - - if categories[1].ID == 0 { - t.Fatalf(`Invalid categoryID, got "%v"`, categories[0].ID) - } - - if categories[1].UserID <= 0 { - t.Fatalf(`Invalid userID, got "%v"`, categories[1].UserID) - } - - if categories[1].Title != categoryName { - t.Fatalf(`Invalid title, got "%v" instead of "%v"`, categories[1].Title, categoryName) - } -} - -func TestDeleteCategory(t *testing.T) { - client := createClient(t) - - category, err := client.CreateCategory("My category") - if err != nil { - t.Fatal(err) - } - - err = client.DeleteCategory(category.ID) - if err != nil { - t.Fatal(`Removing a category should not raise any error`) - } -} - -func TestCannotDeleteCategoryOfAnotherUser(t *testing.T) { - client := createClient(t) - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - client = createClient(t) - err = client.DeleteCategory(categories[0].ID) - if err == nil { - t.Fatal(`Removing a category that belongs to another user should be forbidden`) - } -} diff --git a/internal/tests/endpoint_test.go b/internal/tests/endpoint_test.go deleted file mode 100644 index 16b122d2..00000000 --- a/internal/tests/endpoint_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestWithBadEndpoint(t *testing.T) { - client := miniflux.New("bad url", testAdminUsername, testAdminPassword) - _, err := client.Users() - if err == nil { - t.Fatal(`Using a bad URL should raise an error`) - } -} diff --git a/internal/tests/entry_test.go b/internal/tests/entry_test.go deleted file mode 100644 index b0669797..00000000 --- a/internal/tests/entry_test.go +++ /dev/null @@ -1,517 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestGetAllFeedEntries(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - allResults, err := client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatal(err) - } - - if allResults.Total == 0 { - t.Fatal(`Invalid number of entries`) - } - - if allResults.Entries[0].Title == "" { - t.Fatal(`Invalid entry title`) - } - - filteredResults, err := client.FeedEntries(feed.ID, &miniflux.Filter{Limit: 1, Offset: 5}) - if err != nil { - t.Fatal(err) - } - - if allResults.Total != filteredResults.Total { - t.Fatal(`Total should always contains the total number of items regardless of filters`) - } - - if allResults.Entries[0].ID == filteredResults.Entries[0].ID { - t.Fatal(`Filtered entries should be different than previous results`) - } - - filteredResultsByEntryID, err := client.FeedEntries(feed.ID, &miniflux.Filter{AfterEntryID: allResults.Entries[0].ID}) - if err != nil { - t.Fatal(err) - } - - if filteredResultsByEntryID.Entries[0].ID == allResults.Entries[0].ID { - t.Fatal(`The first entry should be filtered out`) - } -} - -func TestGetAllCategoryEntries(t *testing.T) { - client := createClient(t) - _, category := createFeed(t, client) - - allResults, err := client.CategoryEntries(category.ID, nil) - if err != nil { - t.Fatal(err) - } - - if allResults.Total == 0 { - t.Fatal(`Invalid number of entries`) - } - - if allResults.Entries[0].Title == "" { - t.Fatal(`Invalid entry title`) - } - - filteredResults, err := client.CategoryEntries(category.ID, &miniflux.Filter{Limit: 1, Offset: 5}) - if err != nil { - t.Fatal(err) - } - - if allResults.Total != filteredResults.Total { - t.Fatal(`Total should always contains the total number of items regardless of filters`) - } - - if allResults.Entries[0].ID == filteredResults.Entries[0].ID { - t.Fatal(`Filtered entries should be different than previous results`) - } - - filteredResultsByEntryID, err := client.CategoryEntries(category.ID, &miniflux.Filter{AfterEntryID: allResults.Entries[0].ID}) - if err != nil { - t.Fatal(err) - } - - if filteredResultsByEntryID.Entries[0].ID == allResults.Entries[0].ID { - t.Fatal(`The first entry should be filtered out`) - } -} - -func TestGetAllEntries(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - resultWithoutSorting, err := client.Entries(nil) - if err != nil { - t.Fatal(err) - } - - if resultWithoutSorting.Total == 0 { - t.Fatal(`Invalid number of entries`) - } - - resultWithStatusFilter, err := client.Entries(&miniflux.Filter{Status: miniflux.EntryStatusRead}) - if err != nil { - t.Fatal(err) - } - - if resultWithStatusFilter.Total != 0 { - t.Fatal(`We should have 0 read entries`) - } - - resultWithDifferentSorting, err := client.Entries(&miniflux.Filter{Order: "published_at", Direction: "desc"}) - if err != nil { - t.Fatal(err) - } - - if resultWithDifferentSorting.Entries[0].Title == resultWithoutSorting.Entries[0].Title { - t.Fatalf(`The items should be sorted differently "%v" vs "%v"`, resultWithDifferentSorting.Entries[0].Title, resultWithoutSorting.Entries[0].Title) - } - - resultWithStarredEntries, err := client.Entries(&miniflux.Filter{Starred: miniflux.FilterOnlyStarred}) - if err != nil { - t.Fatal(err) - } - - if resultWithStarredEntries.Total != 0 { - t.Fatalf(`We are not supposed to have starred entries yet`) - } -} - -func TestFilterEntriesByCategory(t *testing.T) { - client := createClient(t) - category, err := client.CreateCategory("Test Filter by Category") - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: category.ID, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - results, err := client.Entries(&miniflux.Filter{CategoryID: category.ID}) - if err != nil { - t.Fatal(err) - } - - if results.Total == 0 { - t.Fatalf(`We should have more than one entry`) - } - - if results.Entries[0].Feed.Category == nil { - t.Fatalf(`The entry feed category should not be nil`) - } - - if results.Entries[0].Feed.Category.ID != category.ID { - t.Errorf(`Entries should be filtered by category_id=%d`, category.ID) - } -} - -func TestFilterEntriesByFeed(t *testing.T) { - client := createClient(t) - category, err := client.CreateCategory("Test Filter by Feed") - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: category.ID, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - results, err := client.Entries(&miniflux.Filter{FeedID: feedID}) - if err != nil { - t.Fatal(err) - } - - if results.Total == 0 { - t.Fatalf(`We should have more than one entry`) - } - - if results.Entries[0].Feed.Category == nil { - t.Fatalf(`The entry feed category should not be nil`) - } - - if results.Entries[0].Feed.Category.ID != category.ID { - t.Errorf(`Entries should be filtered by category_id=%d`, category.ID) - } -} - -func TestFilterEntriesByStatuses(t *testing.T) { - client := createClient(t) - category, err := client.CreateCategory("Test Filter by statuses") - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: category.ID, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - results, err := client.Entries(&miniflux.Filter{FeedID: feedID}) - if err != nil { - t.Fatal(err) - } - - if err := client.UpdateEntries([]int64{results.Entries[0].ID}, miniflux.EntryStatusRead); err != nil { - t.Fatal(err) - } - - if err := client.UpdateEntries([]int64{results.Entries[1].ID}, miniflux.EntryStatusRemoved); err != nil { - t.Fatal(err) - } - - results, err = client.Entries(&miniflux.Filter{Statuses: []string{miniflux.EntryStatusRead, miniflux.EntryStatusRemoved}}) - if err != nil { - t.Fatal(err) - } - - if results.Total != 2 { - t.Fatalf(`We should have 2 entries`) - } - - if results.Entries[0].Status != "read" { - t.Errorf(`The first entry has the wrong status: %s`, results.Entries[0].Status) - } - - if results.Entries[1].Status != "removed" { - t.Errorf(`The 2nd entry has the wrong status: %s`, results.Entries[1].Status) - } -} - -func TestSearchEntries(t *testing.T) { - client := createClient(t) - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - results, err := client.Entries(&miniflux.Filter{Search: "2.0.8"}) - if err != nil { - t.Fatal(err) - } - - if results.Total != 1 { - t.Fatalf(`We should have only one entry instead of %d`, results.Total) - } -} - -func TestInvalidFilters(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - _, err := client.Entries(&miniflux.Filter{Status: "invalid"}) - if err == nil { - t.Fatal(`Using invalid status should raise an error`) - } - - _, err = client.Entries(&miniflux.Filter{Direction: "invalid"}) - if err == nil { - t.Fatal(`Using invalid direction should raise an error`) - } - - _, err = client.Entries(&miniflux.Filter{Order: "invalid"}) - if err == nil { - t.Fatal(`Using invalid order should raise an error`) - } -} - -func TestGetFeedEntry(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - // Test get entry by entry id and feed id - entry, err := client.FeedEntry(result.Entries[0].FeedID, result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - if entry.ID != result.Entries[0].ID { - t.Fatal("Wrong entry returned") - } -} - -func TestGetCategoryEntry(t *testing.T) { - client := createClient(t) - _, category := createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - // Test get entry by entry id and category id - entry, err := client.CategoryEntry(category.ID, result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - if entry.ID != result.Entries[0].ID { - t.Fatal("Wrong entry returned") - } -} - -func TestGetEntry(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - // Test get entry by entry id only - entry, err := client.Entry(result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - if entry.ID != result.Entries[0].ID { - t.Fatal("Wrong entry returned") - } -} - -func TestUpdateStatus(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - err = client.UpdateEntries([]int64{result.Entries[0].ID}, miniflux.EntryStatusRead) - if err != nil { - t.Fatal(err) - } - - entry, err := client.Entry(result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - - if entry.Status != miniflux.EntryStatusRead { - t.Fatal("The entry status should be updated") - } - - err = client.UpdateEntries([]int64{result.Entries[0].ID}, "invalid") - if err == nil { - t.Fatal(`Invalid entry status should not be accepted`) - } - - err = client.UpdateEntries([]int64{}, miniflux.EntryStatusRead) - if err == nil { - t.Fatal(`An empty list of entry should not be accepted`) - } -} - -func TestUpdateEntry(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - title := "New title" - content := "New content" - - _, err = client.UpdateEntry(result.Entries[0].ID, &miniflux.EntryModificationRequest{ - Title: &title, - Content: &content, - }) - if err != nil { - t.Fatal(err) - } - - entry, err := client.Entry(result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - - if entry.Title != title { - t.Fatal("The entry title should be updated") - } - - if entry.Content != content { - t.Fatal("The entry content should be updated") - } -} - -func TestToggleBookmark(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - if result.Entries[0].Starred { - t.Fatal("The entry should not be starred") - } - - err = client.ToggleBookmark(result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - - entry, err := client.Entry(result.Entries[0].ID) - if err != nil { - t.Fatal(err) - } - - if !entry.Starred { - t.Fatal("The entry should be starred") - } -} - -func TestHistoryOrder(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 3}) - if err != nil { - t.Fatal(err) - } - - selectedEntryID := result.Entries[2].ID - - err = client.UpdateEntries([]int64{selectedEntryID}, miniflux.EntryStatusRead) - if err != nil { - t.Fatal(err) - } - - history, err := client.Entries(&miniflux.Filter{Order: "changed_at", Direction: "desc", Limit: 1}) - if err != nil { - t.Fatal(err) - } - - if history.Entries[0].ID != selectedEntryID { - t.Fatal("The entry that we just read should be at the top of the history") - } -} - -func TestFlushHistory(t *testing.T) { - client := createClient(t) - createFeed(t, client) - - result, err := client.Entries(&miniflux.Filter{Limit: 1}) - if err != nil { - t.Fatal(err) - } - - selectedEntryID := result.Entries[0].ID - - err = client.UpdateEntries([]int64{selectedEntryID}, miniflux.EntryStatusRead) - if err != nil { - t.Fatal(err) - } - - err = client.FlushHistory() - if err != nil { - t.Fatal(err) - } - - history, err := client.Entries(&miniflux.Filter{Status: miniflux.EntryStatusRemoved}) - if err != nil { - t.Fatal(err) - } - - if history.Entries[0].ID != selectedEntryID { - t.Fatal("The entry that we just read should have the removed status") - } -} diff --git a/internal/tests/feed_test.go b/internal/tests/feed_test.go deleted file mode 100644 index bf799cec..00000000 --- a/internal/tests/feed_test.go +++ /dev/null @@ -1,880 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "strings" - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestCreateFeed(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - if feed.ID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feed.ID) - } -} - -func TestCannotCreateDuplicatedFeed(t *testing.T) { - client := createClient(t) - feed, category := createFeed(t, client) - - _, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: feed.FeedURL, - CategoryID: category.ID, - }) - if err == nil { - t.Fatal(`Duplicated feeds should not be allowed`) - } -} - -func TestCreateFeedWithInexistingCategory(t *testing.T) { - client := createClient(t) - _, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: -1, - }) - if err == nil { - t.Fatal(`Feeds should not be created with inexisting category`) - } -} - -func TestCreateFeedWithEmptyFeedURL(t *testing.T) { - client := createClient(t) - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - _, err = client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: "", - CategoryID: categories[0].ID, - }) - if err == nil { - t.Fatal(`Feeds should not be created with an empty feed URL`) - } -} - -func TestCreateFeedWithInvalidFeedURL(t *testing.T) { - client := createClient(t) - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - _, err = client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: "invalid", - CategoryID: categories[0].ID, - }) - if err == nil { - t.Fatal(`Feeds should not be created with an invalid feed URL`) - } -} - -func TestCreateDisabledFeed(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - Disabled: true, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if !feed.Disabled { - t.Error(`The feed should be disabled`) - } -} - -func TestCreateFeedWithDisabledCache(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - IgnoreHTTPCache: true, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if !feed.IgnoreHTTPCache { - t.Error(`The feed should be ignoring HTTP cache`) - } -} - -func TestCreateFeedWithCrawlerEnabled(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - Crawler: true, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if !feed.Crawler { - t.Error(`The feed should have the scraper enabled`) - } -} - -func TestCreateFeedWithSelfSignedCertificatesAllowed(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - AllowSelfSignedCertificates: true, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if !feed.AllowSelfSignedCertificates { - t.Error(`The feed should have self-signed certificates enabled`) - } -} - -func TestCreateFeedWithScraperRule(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - ScraperRules: "article", - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if feed.ScraperRules != "article" { - t.Error(`The feed should have the custom scraper rule saved`) - } -} - -func TestCreateFeedWithKeeplistRule(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - KeeplistRules: "(?i)miniflux", - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - if feed.KeeplistRules != "(?i)miniflux" { - t.Error(`The feed should have the custom keep list rule saved`) - } -} - -func TestCreateFeedWithInvalidBlocklistRule(t *testing.T) { - client := createClient(t) - - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - _, err = client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - BlocklistRules: "[", - }) - if err == nil { - t.Fatal(`Feed with invalid block list rule should not be created`) - } -} - -func TestUpdateFeedURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "https://www.example.org/feed.xml" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{FeedURL: &url}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.FeedURL != url { - t.Fatalf(`Wrong FeedURL, got %q instead of %q`, updatedFeed.FeedURL, url) - } -} - -func TestUpdateFeedWithEmptyFeedURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "" - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{FeedURL: &url}); err == nil { - t.Error(`Updating a feed with an empty feed URL should not be possible`) - } -} - -func TestUpdateFeedWithInvalidFeedURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "invalid" - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{FeedURL: &url}); err == nil { - t.Error(`Updating a feed with an invalid feed URL should not be possible`) - } -} - -func TestUpdateFeedSiteURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "https://www.example.org/" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{SiteURL: &url}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.SiteURL != url { - t.Fatalf(`Wrong SiteURL, got %q instead of %q`, updatedFeed.SiteURL, url) - } -} - -func TestUpdateFeedWithEmptySiteURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "" - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{SiteURL: &url}); err == nil { - t.Error(`Updating a feed with an empty site URL should not be possible`) - } -} - -func TestUpdateFeedWithInvalidSiteURL(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - url := "invalid" - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{SiteURL: &url}); err == nil { - t.Error(`Updating a feed with an invalid site URL should not be possible`) - } -} - -func TestUpdateFeedTitle(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - newTitle := "My new feed" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Title: &newTitle}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Title != newTitle { - t.Fatalf(`Wrong title, got %q instead of %q`, updatedFeed.Title, newTitle) - } -} - -func TestUpdateFeedWithEmptyTitle(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - title := "" - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Title: &title}); err == nil { - t.Error(`Updating a feed with an empty title should not be possible`) - } -} - -func TestUpdateFeedCrawler(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - crawler := true - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Crawler: &crawler}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Crawler != crawler { - t.Fatalf(`Wrong crawler value, got "%v" instead of "%v"`, updatedFeed.Crawler, crawler) - } - - if updatedFeed.Title != feed.Title { - t.Fatalf(`The titles should be the same after update`) - } - - crawler = false - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Crawler: &crawler}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Crawler != crawler { - t.Fatalf(`Wrong crawler value, got "%v" instead of "%v"`, updatedFeed.Crawler, crawler) - } -} - -func TestUpdateFeedAllowSelfSignedCertificates(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - selfSigned := true - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{AllowSelfSignedCertificates: &selfSigned}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.AllowSelfSignedCertificates != selfSigned { - t.Fatalf(`Wrong AllowSelfSignedCertificates value, got "%v" instead of "%v"`, updatedFeed.AllowSelfSignedCertificates, selfSigned) - } - - selfSigned = false - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{AllowSelfSignedCertificates: &selfSigned}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.AllowSelfSignedCertificates != selfSigned { - t.Fatalf(`Wrong AllowSelfSignedCertificates value, got "%v" instead of "%v"`, updatedFeed.AllowSelfSignedCertificates, selfSigned) - } -} - -func TestUpdateFeedScraperRules(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - scraperRules := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{ScraperRules: &scraperRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.ScraperRules != scraperRules { - t.Fatalf(`Wrong ScraperRules value, got "%v" instead of "%v"`, updatedFeed.ScraperRules, scraperRules) - } - - scraperRules = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{ScraperRules: &scraperRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.ScraperRules != scraperRules { - t.Fatalf(`Wrong ScraperRules value, got "%v" instead of "%v"`, updatedFeed.ScraperRules, scraperRules) - } -} - -func TestUpdateFeedRewriteRules(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - rewriteRules := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{RewriteRules: &rewriteRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.RewriteRules != rewriteRules { - t.Fatalf(`Wrong RewriteRules value, got "%v" instead of "%v"`, updatedFeed.RewriteRules, rewriteRules) - } - - rewriteRules = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{RewriteRules: &rewriteRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.RewriteRules != rewriteRules { - t.Fatalf(`Wrong RewriteRules value, got "%v" instead of "%v"`, updatedFeed.RewriteRules, rewriteRules) - } -} - -func TestUpdateFeedKeeplistRules(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - keeplistRules := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{KeeplistRules: &keeplistRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.KeeplistRules != keeplistRules { - t.Fatalf(`Wrong KeeplistRules value, got "%v" instead of "%v"`, updatedFeed.KeeplistRules, keeplistRules) - } - - keeplistRules = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{KeeplistRules: &keeplistRules}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.KeeplistRules != keeplistRules { - t.Fatalf(`Wrong KeeplistRules value, got "%v" instead of "%v"`, updatedFeed.KeeplistRules, keeplistRules) - } -} - -func TestUpdateFeedUserAgent(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - userAgent := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{UserAgent: &userAgent}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.UserAgent != userAgent { - t.Fatalf(`Wrong UserAgent value, got "%v" instead of "%v"`, updatedFeed.UserAgent, userAgent) - } - - userAgent = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{UserAgent: &userAgent}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.UserAgent != userAgent { - t.Fatalf(`Wrong UserAgent value, got "%v" instead of "%v"`, updatedFeed.UserAgent, userAgent) - } -} - -func TestUpdateFeedCookie(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - cookie := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Cookie: &cookie}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Cookie != cookie { - t.Fatalf(`Wrong Cookie value, got "%v" instead of "%v"`, updatedFeed.Cookie, cookie) - } - - cookie = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Cookie: &cookie}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Cookie != cookie { - t.Fatalf(`Wrong Cookie value, got "%v" instead of "%v"`, updatedFeed.Cookie, cookie) - } -} - -func TestUpdateFeedUsername(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - username := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Username: &username}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Username != username { - t.Fatalf(`Wrong Username value, got "%v" instead of "%v"`, updatedFeed.Username, username) - } - - username = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Username: &username}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Username != username { - t.Fatalf(`Wrong Username value, got "%v" instead of "%v"`, updatedFeed.Username, username) - } -} - -func TestUpdateFeedPassword(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - password := "test" - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Password: &password}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Password != password { - t.Fatalf(`Wrong Password value, got "%v" instead of "%v"`, updatedFeed.Password, password) - } - - password = "" - updatedFeed, err = client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{Password: &password}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Password != password { - t.Fatalf(`Wrong Password value, got "%v" instead of "%v"`, updatedFeed.Password, password) - } -} - -func TestUpdateFeedCategory(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - newCategory, err := client.CreateCategory("my new category") - if err != nil { - t.Fatal(err) - } - - updatedFeed, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{CategoryID: &newCategory.ID}) - if err != nil { - t.Fatal(err) - } - - if updatedFeed.Category.ID != newCategory.ID { - t.Fatalf(`Wrong CategoryID value, got "%v" instead of "%v"`, updatedFeed.Category.ID, newCategory.ID) - } -} - -func TestUpdateFeedWithEmptyCategoryID(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - categoryID := int64(0) - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{CategoryID: &categoryID}); err == nil { - t.Error(`Updating a feed with an empty category should not be possible`) - } -} - -func TestUpdateFeedWithInvalidCategoryID(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - - categoryID := int64(-1) - if _, err := client.UpdateFeed(feed.ID, &miniflux.FeedModificationRequest{CategoryID: &categoryID}); err == nil { - t.Error(`Updating a feed with an invalid category should not be possible`) - } -} - -func TestMarkFeedAsRead(t *testing.T) { - client := createClient(t) - - feed, _ := createFeed(t, client) - - results, err := client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get entries: %v`, err) - } - if results.Total == 0 { - t.Fatalf(`Invalid number of entries: %d`, results.Total) - } - if results.Entries[0].Status != miniflux.EntryStatusUnread { - t.Fatalf(`Invalid entry status, got %q instead of %q`, results.Entries[0].Status, miniflux.EntryStatusUnread) - } - - if err := client.MarkFeedAsRead(feed.ID); err != nil { - t.Fatalf(`Failed to mark feed as read: %v`, err) - } - - results, err = client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get updated entries: %v`, err) - } - - for _, entry := range results.Entries { - if entry.Status != miniflux.EntryStatusRead { - t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) - } - } -} - -func TestFetchCounters(t *testing.T) { - client := createClient(t) - - feed, _ := createFeed(t, client) - - results, err := client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get entries: %v`, err) - } - - counters, err := client.FetchCounters() - if err != nil { - t.Fatalf(`Failed to fetch unread count: %v`, err) - } - unreadCounter, exists := counters.UnreadCounters[feed.ID] - if !exists { - unreadCounter = 0 - } - - unreadExpected := 0 - for _, entry := range results.Entries { - if entry.Status == miniflux.EntryStatusUnread { - unreadExpected++ - } - } - - if unreadExpected != unreadCounter { - t.Errorf(`Expected %d unread entries but %d instead`, unreadExpected, unreadCounter) - } -} - -func TestDeleteFeed(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - if err := client.DeleteFeed(feed.ID); err != nil { - t.Fatal(err) - } -} - -func TestRefreshFeed(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - if err := client.RefreshFeed(feed.ID); err != nil { - t.Fatal(err) - } -} - -func TestGetFeed(t *testing.T) { - client := createClient(t) - feed, category := createFeed(t, client) - - if feed.Title != testFeedTitle { - t.Fatalf(`Invalid feed title, got "%v" instead of "%v"`, feed.Title, testFeedTitle) - } - - if feed.SiteURL != testWebsiteURL { - t.Fatalf(`Invalid site URL, got "%v" instead of "%v"`, feed.SiteURL, testWebsiteURL) - } - - if feed.FeedURL != testFeedURL { - t.Fatalf(`Invalid feed URL, got "%v" instead of "%v"`, feed.FeedURL, testFeedURL) - } - - if feed.Category.ID != category.ID { - t.Fatalf(`Invalid feed category ID, got "%v" instead of "%v"`, feed.Category.ID, category.ID) - } - - if feed.Category.UserID != category.UserID { - t.Fatalf(`Invalid feed category user ID, got "%v" instead of "%v"`, feed.Category.UserID, category.UserID) - } - - if feed.Category.Title != category.Title { - t.Fatalf(`Invalid feed category title, got "%v" instead of "%v"`, feed.Category.Title, category.Title) - } -} - -func TestGetFeedIcon(t *testing.T) { - client := createClient(t) - feed, _ := createFeed(t, client) - feedIcon, err := client.FeedIcon(feed.ID) - if err != nil { - t.Fatal(err) - } - - if feedIcon.ID == 0 { - t.Fatalf(`Invalid feed icon ID, got "%d"`, feedIcon.ID) - } - - expectedMimeType := "image/x-icon" - if feedIcon.MimeType != expectedMimeType { - t.Fatalf(`Invalid feed icon mime type, got %q instead of %q`, feedIcon.MimeType, expectedMimeType) - } - - if !strings.HasPrefix(feedIcon.Data, expectedMimeType) { - t.Fatalf(`Invalid feed icon data, got "%v"`, feedIcon.Data) - } - - feedIcon, err = client.Icon(feedIcon.ID) - if err != nil { - t.Fatal(err) - } - - if feedIcon.MimeType != expectedMimeType { - t.Fatalf(`Invalid feed icon mime type, got %q instead of %q`, feedIcon.MimeType, expectedMimeType) - } - - if !strings.HasPrefix(feedIcon.Data, expectedMimeType) { - t.Fatalf(`Invalid feed icon data, got "%v"`, feedIcon.Data) - } -} - -func TestGetFeedIconNotFound(t *testing.T) { - client := createClient(t) - if _, err := client.FeedIcon(42); err == nil { - t.Fatalf(`The feed icon should be null`) - } -} - -func TestGetFeeds(t *testing.T) { - client := createClient(t) - feed, category := createFeed(t, client) - - feeds, err := client.Feeds() - if err != nil { - t.Fatal(err) - } - - if len(feeds) != 1 { - t.Fatalf(`Invalid number of feeds`) - } - - if feeds[0].ID != feed.ID { - t.Fatalf(`Invalid feed ID, got "%v" instead of "%v"`, feeds[0].ID, feed.ID) - } - - if feeds[0].Title != testFeedTitle { - t.Fatalf(`Invalid feed title, got "%v" instead of "%v"`, feeds[0].Title, testFeedTitle) - } - - if feeds[0].SiteURL != testWebsiteURL { - t.Fatalf(`Invalid site URL, got "%v" instead of "%v"`, feeds[0].SiteURL, testWebsiteURL) - } - - if feeds[0].FeedURL != testFeedURL { - t.Fatalf(`Invalid feed URL, got "%v" instead of "%v"`, feeds[0].FeedURL, testFeedURL) - } - - if feeds[0].Category.ID != category.ID { - t.Fatalf(`Invalid feed category ID, got "%v" instead of "%v"`, feeds[0].Category.ID, category.ID) - } - - if feeds[0].Category.UserID != category.UserID { - t.Fatalf(`Invalid feed category user ID, got "%v" instead of "%v"`, feeds[0].Category.UserID, category.UserID) - } - - if feeds[0].Category.Title != category.Title { - t.Fatalf(`Invalid feed category title, got "%v" instead of "%v"`, feeds[0].Category.Title, category.Title) - } -} - -func TestGetFeedsByCategory(t *testing.T) { - client := createClient(t) - feed, category := createFeed(t, client) - - feeds, err := client.CategoryFeeds(category.ID) - if err != nil { - t.Fatal(err) - } - - if len(feeds) != 1 { - t.Fatalf(`Invalid number of feeds`) - } - - if feeds[0].ID != feed.ID { - t.Fatalf(`Invalid feed ID, got "%v" instead of "%v"`, feeds[0].ID, feed.ID) - } - - if feeds[0].Title != testFeedTitle { - t.Fatalf(`Invalid feed title, got "%v" instead of "%v"`, feeds[0].Title, testFeedTitle) - } - - if feeds[0].SiteURL != testWebsiteURL { - t.Fatalf(`Invalid site URL, got "%v" instead of "%v"`, feeds[0].SiteURL, testWebsiteURL) - } - - if feeds[0].FeedURL != testFeedURL { - t.Fatalf(`Invalid feed URL, got "%v" instead of "%v"`, feeds[0].FeedURL, testFeedURL) - } - - if feeds[0].Category.ID != category.ID { - t.Fatalf(`Invalid feed category ID, got "%v" instead of "%v"`, feeds[0].Category.ID, category.ID) - } - - if feeds[0].Category.UserID != category.UserID { - t.Fatalf(`Invalid feed category user ID, got "%v" instead of "%v"`, feeds[0].Category.UserID, category.UserID) - } - - if feeds[0].Category.Title != category.Title { - t.Fatalf(`Invalid feed category title, got "%v" instead of "%v"`, feeds[0].Category.Title, category.Title) - } -} diff --git a/internal/tests/import_export_test.go b/internal/tests/import_export_test.go deleted file mode 100644 index b30e6acd..00000000 --- a/internal/tests/import_export_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "bytes" - "io" - "strings" - "testing" -) - -func TestExport(t *testing.T) { - client := createClient(t) - - output, err := client.Export() - if err != nil { - t.Fatal(err) - } - - if !strings.HasPrefix(string(output), " - - - - - - - ` - - b := bytes.NewReader([]byte(data)) - err := client.Import(io.NopCloser(b)) - if err != nil { - t.Fatal(err) - } -} diff --git a/internal/tests/subscription_test.go b/internal/tests/subscription_test.go deleted file mode 100644 index d9ed9311..00000000 --- a/internal/tests/subscription_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestDiscoverSubscriptions(t *testing.T) { - client := createClient(t) - subscriptions, err := client.Discover(testWebsiteURL) - if err != nil { - t.Fatal(err) - } - - if len(subscriptions) != 1 { - t.Fatalf(`Invalid number of subscriptions, got "%v" instead of "%v"`, len(subscriptions), 2) - } - - if subscriptions[0].Title != testSubscriptionTitle { - t.Fatalf(`Invalid feed title, got "%v" instead of "%v"`, subscriptions[0].Title, testSubscriptionTitle) - } - - if subscriptions[0].Type != "atom" { - t.Fatalf(`Invalid feed type, got "%v" instead of "%v"`, subscriptions[0].Type, "atom") - } - - if subscriptions[0].URL != testFeedURL { - t.Fatalf(`Invalid feed URL, got "%v" instead of "%v"`, subscriptions[0].URL, testFeedURL) - } -} - -func TestDiscoverSubscriptionsWithInvalidURL(t *testing.T) { - client := createClient(t) - _, err := client.Discover("invalid") - if err == nil { - t.Fatal(`Invalid URLs should be rejected`) - } -} - -func TestDiscoverSubscriptionsWithNoSubscription(t *testing.T) { - client := createClient(t) - _, err := client.Discover(testBaseURL) - if err != miniflux.ErrNotFound { - t.Fatal(`A 404 should be returned when there is no subscription`) - } -} diff --git a/internal/tests/tests.go b/internal/tests/tests.go deleted file mode 100644 index ac173e4b..00000000 --- a/internal/tests/tests.go +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "fmt" - "math" - "math/rand" - "testing" - - miniflux "miniflux.app/v2/client" -) - -const ( - testBaseURL = "http://127.0.0.1:8080/" - testAdminUsername = "admin" - testAdminPassword = "test123" - testStandardPassword = "secret" - testFeedURL = "https://miniflux.app/feed.xml" - testFeedTitle = "Miniflux" - testSubscriptionTitle = "Miniflux Releases" - testWebsiteURL = "https://miniflux.app" -) - -func getRandomUsername() string { - return fmt.Sprintf("user%10d", rand.Intn(math.MaxInt64)) -} - -func createClient(t *testing.T) *miniflux.Client { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - _, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - return miniflux.New(testBaseURL, username, testStandardPassword) -} - -func createFeed(t *testing.T, client *miniflux.Client) (*miniflux.Feed, *miniflux.Category) { - categories, err := client.Categories() - if err != nil { - t.Fatal(err) - } - - feedID, err := client.CreateFeed(&miniflux.FeedCreationRequest{ - FeedURL: testFeedURL, - CategoryID: categories[0].ID, - }) - if err != nil { - t.Fatal(err) - } - - if feedID == 0 { - t.Fatalf(`Invalid feed ID, got %q`, feedID) - } - - feed, err := client.Feed(feedID) - if err != nil { - t.Fatal(err) - } - - return feed, categories[0] -} diff --git a/internal/tests/user_test.go b/internal/tests/user_test.go deleted file mode 100644 index 6952bc06..00000000 --- a/internal/tests/user_test.go +++ /dev/null @@ -1,715 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestWithWrongCredentials(t *testing.T) { - client := miniflux.New(testBaseURL, "invalid", "invalid") - _, err := client.Users() - if err == nil { - t.Fatal(`Using bad credentials should raise an error`) - } - - if err != miniflux.ErrNotAuthorized { - t.Fatal(`A "Not Authorized" error should be raised`) - } -} - -func TestGetCurrentLoggedUser(t *testing.T) { - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.Me() - if err != nil { - t.Fatal(err) - } - - if user.ID == 0 { - t.Fatalf(`Invalid userID, got %q`, user.ID) - } - - if user.Username != testAdminUsername { - t.Fatalf(`Invalid username, got %q`, user.Username) - } -} - -func TestGetUsers(t *testing.T) { - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - users, err := client.Users() - if err != nil { - t.Fatal(err) - } - - if len(users) == 0 { - t.Fatal("The list of users is empty") - } - - if users[0].ID == 0 { - t.Fatalf(`Invalid userID, got "%v"`, users[0].ID) - } - - if users[0].Username != testAdminUsername { - t.Fatalf(`Invalid username, got "%v" instead of "%v"`, users[0].Username, testAdminUsername) - } - - if users[0].Password != "" { - t.Fatalf(`Invalid password, got "%v"`, users[0].Password) - } - - if users[0].Language != "en_US" { - t.Fatalf(`Invalid language, got "%v"`, users[0].Language) - } - - if users[0].Theme != "light_serif" { - t.Fatalf(`Invalid theme, got "%v"`, users[0].Theme) - } - - if users[0].Timezone != "UTC" { - t.Fatalf(`Invalid timezone, got "%v"`, users[0].Timezone) - } - - if !users[0].IsAdmin { - t.Fatalf(`Invalid role, got "%v"`, users[0].IsAdmin) - } - - if users[0].EntriesPerPage != 100 { - t.Fatalf(`Invalid entries per page, got "%v"`, users[0].EntriesPerPage) - } - - if users[0].DisplayMode != "standalone" { - t.Fatalf(`Invalid web app display mode, got "%v"`, users[0].DisplayMode) - } - - if users[0].GestureNav != "tap" { - t.Fatalf(`Invalid gesture navigation, got "%v"`, users[0].GestureNav) - } - - if users[0].DefaultReadingSpeed != 265 { - t.Fatalf(`Invalid default reading speed, got "%v"`, users[0].DefaultReadingSpeed) - } - - if users[0].CJKReadingSpeed != 500 { - t.Fatalf(`Invalid cjk reading speed, got "%v"`, users[0].CJKReadingSpeed) - } -} - -func TestCreateStandardUser(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - if user.ID == 0 { - t.Fatalf(`Invalid userID, got "%v"`, user.ID) - } - - if user.Username != username { - t.Fatalf(`Invalid username, got "%v" instead of "%v"`, user.Username, username) - } - - if user.Password != "" { - t.Fatalf(`Invalid password, got "%v"`, user.Password) - } - - if user.Language != "en_US" { - t.Fatalf(`Invalid language, got "%v"`, user.Language) - } - - if user.Theme != "light_serif" { - t.Fatalf(`Invalid theme, got "%v"`, user.Theme) - } - - if user.Timezone != "UTC" { - t.Fatalf(`Invalid timezone, got "%v"`, user.Timezone) - } - - if user.IsAdmin { - t.Fatalf(`Invalid role, got "%v"`, user.IsAdmin) - } - - if user.LastLoginAt != nil { - t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) - } - - if user.EntriesPerPage != 100 { - t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage) - } - - if user.DisplayMode != "standalone" { - t.Fatalf(`Invalid web app display mode, got "%v"`, user.DisplayMode) - } - - if user.DefaultReadingSpeed != 265 { - t.Fatalf(`Invalid default reading speed, got "%v"`, user.DefaultReadingSpeed) - } - - if user.CJKReadingSpeed != 500 { - t.Fatalf(`Invalid cjk reading speed, got "%v"`, user.CJKReadingSpeed) - } -} - -func TestRemoveUser(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - if err := client.DeleteUser(user.ID); err != nil { - t.Fatalf(`Unable to remove user: "%v"`, err) - } -} - -func TestGetUserByID(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - _, err = client.UserByID(99999) - if err == nil { - t.Fatal(`Should returns a 404`) - } - - user, err = client.UserByID(user.ID) - if err != nil { - t.Fatal(err) - } - - if user.ID == 0 { - t.Fatalf(`Invalid userID, got "%v"`, user.ID) - } - - if user.Username != username { - t.Fatalf(`Invalid username, got "%v" instead of "%v"`, user.Username, username) - } - - if user.Password != "" { - t.Fatalf(`Invalid password, got "%v"`, user.Password) - } - - if user.Language != "en_US" { - t.Fatalf(`Invalid language, got "%v"`, user.Language) - } - - if user.Theme != "light_serif" { - t.Fatalf(`Invalid theme, got "%v"`, user.Theme) - } - - if user.Timezone != "UTC" { - t.Fatalf(`Invalid timezone, got "%v"`, user.Timezone) - } - - if user.IsAdmin { - t.Fatalf(`Invalid role, got "%v"`, user.IsAdmin) - } - - if user.LastLoginAt != nil { - t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) - } - - if user.EntriesPerPage != 100 { - t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage) - } - - if user.DisplayMode != "standalone" { - t.Fatalf(`Invalid web app display mode, got "%v"`, user.DisplayMode) - } - - if user.DefaultReadingSpeed != 265 { - t.Fatalf(`Invalid default reading speed, got "%v"`, user.DefaultReadingSpeed) - } - - if user.CJKReadingSpeed != 500 { - t.Fatalf(`Invalid cjk reading speed, got "%v"`, user.CJKReadingSpeed) - } -} - -func TestGetUserByUsername(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - _, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - _, err = client.UserByUsername("missinguser") - if err == nil { - t.Fatal(`Should returns a 404`) - } - - user, err := client.UserByUsername(username) - if err != nil { - t.Fatal(err) - } - - if user.ID == 0 { - t.Fatalf(`Invalid userID, got "%v"`, user.ID) - } - - if user.Username != username { - t.Fatalf(`Invalid username, got "%v" instead of "%v"`, user.Username, username) - } - - if user.Password != "" { - t.Fatalf(`Invalid password, got "%v"`, user.Password) - } - - if user.Language != "en_US" { - t.Fatalf(`Invalid language, got "%v"`, user.Language) - } - - if user.Theme != "light_serif" { - t.Fatalf(`Invalid theme, got "%v"`, user.Theme) - } - - if user.Timezone != "UTC" { - t.Fatalf(`Invalid timezone, got "%v"`, user.Timezone) - } - - if user.IsAdmin { - t.Fatalf(`Invalid role, got "%v"`, user.IsAdmin) - } - - if user.LastLoginAt != nil { - t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) - } - - if user.EntriesPerPage != 100 { - t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage) - } - - if user.DisplayMode != "standalone" { - t.Fatalf(`Invalid web app display mode, got "%v"`, user.DisplayMode) - } - - if user.DefaultReadingSpeed != 265 { - t.Fatalf(`Invalid default reading speed, got "%v"`, user.DefaultReadingSpeed) - } - - if user.CJKReadingSpeed != 500 { - t.Fatalf(`Invalid cjk reading speed, got "%v"`, user.CJKReadingSpeed) - } -} - -func TestUpdateUserTheme(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - theme := "dark_serif" - user, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Theme: &theme}) - if err != nil { - t.Fatal(err) - } - - if user.Theme != theme { - t.Fatalf(`Unable to update user Theme: got "%v" instead of "%v"`, user.Theme, theme) - } -} - -func TestUpdateUserFields(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - stylesheet := "body { color: red }" - swipe := false - entriesPerPage := 5 - displayMode := "fullscreen" - defaultReadingSpeed := 380 - cjkReadingSpeed := 200 - user, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{ - Stylesheet: &stylesheet, - EntrySwipe: &swipe, - EntriesPerPage: &entriesPerPage, - DisplayMode: &displayMode, - DefaultReadingSpeed: &defaultReadingSpeed, - CJKReadingSpeed: &cjkReadingSpeed, - }) - if err != nil { - t.Fatal(err) - } - - if user.Stylesheet != stylesheet { - t.Fatalf(`Unable to update user stylesheet: got %q instead of %q`, user.Stylesheet, stylesheet) - } - - if user.EntrySwipe != swipe { - t.Fatalf(`Unable to update user EntrySwipe: got %v instead of %v`, user.EntrySwipe, swipe) - } - - if user.EntriesPerPage != entriesPerPage { - t.Fatalf(`Unable to update user EntriesPerPage: got %q instead of %q`, user.EntriesPerPage, entriesPerPage) - } - - if user.DisplayMode != displayMode { - t.Fatalf(`Unable to update user DisplayMode: got %q instead of %q`, user.DisplayMode, displayMode) - } - - if user.DefaultReadingSpeed != defaultReadingSpeed { - t.Fatalf(`Invalid default reading speed, got %v instead of %v`, user.DefaultReadingSpeed, defaultReadingSpeed) - } - - if user.CJKReadingSpeed != cjkReadingSpeed { - t.Fatalf(`Invalid cjk reading speed, got %v instead of %v`, user.CJKReadingSpeed, cjkReadingSpeed) - } -} - -func TestUpdateUserThemeWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - theme := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Theme: &theme}) - if err == nil { - t.Fatal(`Updating a user Theme with an invalid value should raise an error`) - } -} - -func TestUpdateUserLanguageWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - language := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Language: &language}) - if err == nil { - t.Fatal(`Updating a user language with an invalid value should raise an error`) - } -} - -func TestUpdateUserTimezoneWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - timezone := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Timezone: &timezone}) - if err == nil { - t.Fatal(`Updating a user timezone with an invalid value should raise an error`) - } -} - -func TestUpdateUserEntriesPerPageWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - entriesPerPage := -5 - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{EntriesPerPage: &entriesPerPage}) - if err == nil { - t.Fatal(`Updating a user EntriesPerPage with an invalid value should raise an error`) - } -} - -func TestUpdateUserEntryDirectionWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - entryDirection := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{EntryDirection: &entryDirection}) - if err == nil { - t.Fatal(`Updating a user EntryDirection with an invalid value should raise an error`) - } -} - -func TestUpdateUserEntryOrderWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - entryOrder := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{EntryOrder: &entryOrder}) - if err == nil { - t.Fatal(`Updating a user EntryOrder with an invalid value should raise an error`) - } -} - -func TestUpdateUserPasswordWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - password := "short" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Password: &password}) - if err == nil { - t.Fatal(`Updating a user password with an invalid value should raise an error`) - } -} - -func TestUpdateUserDisplayModeWithInvalidValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - displayMode := "invalid" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{DisplayMode: &displayMode}) - if err == nil { - t.Fatal(`Updating a user web app display mode with an invalid value should raise an error`) - } -} - -func TestUpdateUserWithEmptyUsernameValue(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - newUsername := "" - _, err = client.UpdateUser(user.ID, &miniflux.UserModificationRequest{Username: &newUsername}) - if err == nil { - t.Fatal(`Updating a user with an empty username should raise an error`) - } -} - -func TestCannotCreateDuplicateUser(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - _, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - _, err = client.CreateUser(username, testStandardPassword, false) - if err == nil { - t.Fatal(`Duplicated users should not be allowed`) - } -} - -func TestCannotListUsersAsNonAdmin(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - _, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - client = miniflux.New(testBaseURL, username, testStandardPassword) - _, err = client.Users() - if err == nil { - t.Fatal(`Standard users should not be able to list any users`) - } - - if err != miniflux.ErrForbidden { - t.Fatal(`A "Forbidden" error should be raised`) - } -} - -func TestCannotGetUserAsNonAdmin(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - client = miniflux.New(testBaseURL, username, testStandardPassword) - _, err = client.UserByID(user.ID) - if err == nil { - t.Fatal(`Standard users should not be able to get any users`) - } - - if err != miniflux.ErrForbidden { - t.Fatal(`A "Forbidden" error should be raised`) - } -} - -func TestCannotUpdateUserAsNonAdmin(t *testing.T) { - adminClient := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - - usernameA := getRandomUsername() - userA, err := adminClient.CreateUser(usernameA, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - usernameB := getRandomUsername() - _, err = adminClient.CreateUser(usernameB, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - entriesPerPage := 10 - userAClient := miniflux.New(testBaseURL, usernameA, testStandardPassword) - userAAfterUpdate, err := userAClient.UpdateUser(userA.ID, &miniflux.UserModificationRequest{EntriesPerPage: &entriesPerPage}) - if err != nil { - t.Fatal(`Standard users should be able to update themselves`) - } - - if userAAfterUpdate.EntriesPerPage != entriesPerPage { - t.Fatalf(`The EntriesPerPage field of this user should be updated`) - } - - isAdmin := true - _, err = userAClient.UpdateUser(userA.ID, &miniflux.UserModificationRequest{IsAdmin: &isAdmin}) - if err == nil { - t.Fatal(`Standard users should not be able to become admin`) - } - - userBClient := miniflux.New(testBaseURL, usernameB, testStandardPassword) - _, err = userBClient.UpdateUser(userA.ID, &miniflux.UserModificationRequest{}) - if err == nil { - t.Fatal(`Standard users should not be able to update other users`) - } - - if err != miniflux.ErrForbidden { - t.Fatal(`A "Forbidden" error should be raised`) - } - - stylesheet := "test" - userC, err := adminClient.UpdateUser(userA.ID, &miniflux.UserModificationRequest{Stylesheet: &stylesheet}) - if err != nil { - t.Fatal(`Admin users should be able to update any users`) - } - - if userC.Stylesheet != stylesheet { - t.Fatalf(`The Stylesheet field of this user should be updated`) - } -} - -func TestCannotCreateUserAsNonAdmin(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - _, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - client = miniflux.New(testBaseURL, username, testStandardPassword) - _, err = client.CreateUser(username, testStandardPassword, false) - if err == nil { - t.Fatal(`Standard users should not be able to create users`) - } - - if err != miniflux.ErrForbidden { - t.Fatal(`A "Forbidden" error should be raised`) - } -} - -func TestCannotDeleteUserAsNonAdmin(t *testing.T) { - username := getRandomUsername() - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := client.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - client = miniflux.New(testBaseURL, username, testStandardPassword) - err = client.DeleteUser(user.ID) - if err == nil { - t.Fatal(`Standard users should not be able to remove any users`) - } - - if err != miniflux.ErrForbidden { - t.Fatal(`A "Forbidden" error should be raised`) - } -} - -func TestMarkUserAsReadAsUser(t *testing.T) { - username := getRandomUsername() - adminClient := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user, err := adminClient.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - - client := miniflux.New(testBaseURL, username, testStandardPassword) - feed, _ := createFeed(t, client) - - results, err := client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get entries: %v`, err) - } - if results.Total == 0 { - t.Fatalf(`Invalid number of entries: %d`, results.Total) - } - if results.Entries[0].Status != miniflux.EntryStatusUnread { - t.Fatalf(`Invalid entry status, got %q instead of %q`, results.Entries[0].Status, miniflux.EntryStatusUnread) - } - - if err := client.MarkAllAsRead(user.ID); err != nil { - t.Fatalf(`Failed to mark user's unread entries as read: %v`, err) - } - - results, err = client.FeedEntries(feed.ID, nil) - if err != nil { - t.Fatalf(`Failed to get updated entries: %v`, err) - } - - for _, entry := range results.Entries { - if entry.Status != miniflux.EntryStatusRead { - t.Errorf(`Status for entry %d was %q instead of %q`, entry.ID, entry.Status, miniflux.EntryStatusRead) - } - } -} - -func TestCannotMarkUserAsReadAsOtherUser(t *testing.T) { - username := getRandomUsername() - adminClient := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - user1, err := adminClient.CreateUser(username, testStandardPassword, false) - if err != nil { - t.Fatal(err) - } - createFeed(t, miniflux.New(testBaseURL, username, testStandardPassword)) - - username2 := getRandomUsername() - if _, err = adminClient.CreateUser(username2, testStandardPassword, false); err != nil { - t.Fatal(err) - } - - client := miniflux.New(testBaseURL, username2, testStandardPassword) - err = client.MarkAllAsRead(user1.ID) - if err == nil { - t.Fatalf(`Non-admin users should not be able to mark another user as read`) - } - if err != miniflux.ErrForbidden { - t.Errorf(`A "Forbidden" error should be raised, got %q`, err) - } -} diff --git a/internal/tests/version_test.go b/internal/tests/version_test.go deleted file mode 100644 index ebb11aa1..00000000 --- a/internal/tests/version_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration -// +build integration - -package tests - -import ( - "testing" - - miniflux "miniflux.app/v2/client" -) - -func TestVersionEndpoint(t *testing.T) { - client := miniflux.New(testBaseURL, testAdminUsername, testAdminPassword) - version, err := client.Version() - if err != nil { - t.Fatal(err) - } - - if version.Version == "" { - t.Fatal(`Version should not be empty`) - } - - if version.Commit == "" { - t.Fatal(`Commit should not be empty`) - } - - if version.BuildDate == "" { - t.Fatal(`Build date should not be empty`) - } - - if version.GoVersion == "" { - t.Fatal(`Go version should not be empty`) - } - - if version.Compiler == "" { - t.Fatal(`Compiler should not be empty`) - } - - if version.Arch == "" { - t.Fatal(`Arch should not be empty`) - } - - if version.OS == "" { - t.Fatal(`OS should not be empty`) - } -}