mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-11 16:24:11 +01:00
Config: Fix assets path and disable hub updates when running unit tests
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PhotoPrism® Repository Guidelines
|
||||
|
||||
**Last Updated:** October 3, 2025
|
||||
**Last Updated:** October 4, 2025
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -247,6 +247,8 @@ If anything in this file conflicts with the `Makefile` or the Developer Guide, t
|
||||
- When adding persistent fixtures (photos, files, labels, etc.), always obtain new IDs via `rnd.GenerateUID(...)` with the matching prefix (`entity.PhotoUID`, `entity.FileUID`, `entity.LabelUID`, …) instead of inventing manual strings so the search helpers recognize them.
|
||||
- For database updates, prefer the `entity.Values` type alias over raw `map[string]interface{}` so helpers stay type-safe and consistent with existing code.
|
||||
- Reach for `config.NewMinimalTestConfig(t.TempDir())` when a test only needs filesystem/config scaffolding, and use `config.NewMinimalTestConfigWithDb("<name>", t.TempDir())` when you need a fresh SQLite schema without the cached fixture snapshot.
|
||||
- Config test helpers now auto-discover the repo `assets/` directory; you should not set `PHOTOPRISM_ASSETS_PATH` manually in package `init()` functions unless you have a non-standard layout.
|
||||
- Hub API traffic is disabled in tests by default via `hub.ApplyTestConfig()`; opt back in with `PHOTOPRISM_TEST_HUB=test`.
|
||||
- Avoid `config.TestConfig()` in new tests unless you truly need the fully seeded fixture set: it shares a singleton instance that runs `InitializeTestData()` and wipes `storage/testdata`. Tests that write to Originals/Import (e.g. WebDAV helpers) should instead call `config.NewMinimalTestConfig(t.TempDir())` (or the DB variant) and follow up with `conf.CreateDirectories()` so they operate on an isolated sandbox.
|
||||
- Shared fixtures live under `storage/testdata`; `NewTestConfig("<pkg>")` already calls `InitializeTestData()`, but call `c.InitializeTestData()` (and optionally `c.AssertTestData(t)`) when you construct custom configs so originals/import/cache/temp exist. `InitializeTestData()` clears old data, downloads fixtures if needed, then calls `CreateDirectories()`.
|
||||
- `PhotoFixtures.Get()` and similar helpers return value copies; when a test needs the database-backed row (with associations preloaded), re-query by UID/ID using helpers like `entity.FindPhoto(fixture)` so updates observe persisted IDs and in-memory caches stay coherent.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
PhotoPrism — Backend CODEMAP
|
||||
|
||||
**Last Updated:** October 2, 2025
|
||||
**Last Updated:** October 4, 2025
|
||||
|
||||
Purpose
|
||||
- Give agents and contributors a fast, reliable map of where things live and how they fit together, so you can add features, fix bugs, and write tests without spelunking.
|
||||
@@ -143,6 +143,8 @@ Testing
|
||||
- CLI tests: `PHOTOPRISM_CLI=noninteractive` or pass `--yes` to avoid prompts; use `RunWithTestContext` to prevent `os.Exit`.
|
||||
- SQLite DSN in tests is per‑suite (not empty). Clean up files if you capture the DSN.
|
||||
- Frontend unit tests via Vitest are separate; see `frontend/CODEMAP.md`.
|
||||
- Config helpers automatically disable Hub service calls for tests (`hub.ApplyTestConfig()`).
|
||||
- Test configs auto-discover the repo `assets/` folder, so avoid adding per-package `PHOTOPRISM_ASSETS_PATH` shims unless you have an unusual layout.
|
||||
|
||||
Security & Hot Spots (Where to Look)
|
||||
- Zip extraction (path traversal prevention): `pkg/fs/zip.go`
|
||||
|
||||
@@ -20,13 +20,6 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Ensure assets path is set so TestMain in this package can initialize config.
|
||||
func init() {
|
||||
if os.Getenv("PHOTOPRISM_ASSETS_PATH") == "" {
|
||||
_ = os.Setenv("PHOTOPRISM_ASSETS_PATH", fs.Abs("../../assets"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Init test logger.
|
||||
log = logrus.StandardLogger()
|
||||
|
||||
@@ -781,7 +781,7 @@ func (c *Config) RenewApiKeysWithToken(token string) error {
|
||||
return i18n.Error(i18n.ErrAccountConnect)
|
||||
}
|
||||
} else if err = c.hub.Save(); err != nil {
|
||||
log.Warnf("config: failed to save api keys for maps and places (%s)", err)
|
||||
log.Warnf("config: failed to save API keys for maps and places (%s)", err)
|
||||
return i18n.Error(i18n.ErrSaveFailed)
|
||||
} else {
|
||||
c.hub.Propagate()
|
||||
|
||||
@@ -10,15 +10,13 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/service/hub"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// Set the assets path so that NewConfig(CliTestContext) always works for the package tests.
|
||||
// Runs first when package is tested.
|
||||
func init() {
|
||||
if os.Getenv("PHOTOPRISM_ASSETS_PATH") == "" {
|
||||
// From internal/config to repo root assets.
|
||||
_ = os.Setenv("PHOTOPRISM_ASSETS_PATH", filepath.Clean("../../assets"))
|
||||
}
|
||||
hub.ApplyTestConfig()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config/customize"
|
||||
"github.com/photoprism/photoprism/internal/service/hub"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
@@ -38,13 +39,20 @@ var testConfigOnce sync.Once
|
||||
var testConfigMutex sync.Mutex
|
||||
var testDataMutex sync.Mutex
|
||||
|
||||
// testDataPath resolves the QA fixture directory that ships with the assets
|
||||
// bundle. Helpers fall back to this location when the caller does not provide
|
||||
// an explicit storage path.
|
||||
func testDataPath(assetsPath string) string {
|
||||
return assetsPath + "/testdata"
|
||||
}
|
||||
|
||||
// PkgNameRegexp normalizes database file names by stripping unsupported
|
||||
// characters from the Go package identifier supplied by tests.
|
||||
var PkgNameRegexp = regexp.MustCompile("[^a-zA-Z\\-_]+")
|
||||
|
||||
// NewTestOptions returns valid config options for tests.
|
||||
// NewTestOptions builds fully-populated Options suited for backend tests. It
|
||||
// creates an isolated storage directory under storage/testdata (or the
|
||||
// PHOTOPRISM_STORAGE_PATH override) and enables all test-friendly defaults.
|
||||
func NewTestOptions(dbName string) *Options {
|
||||
// Find storage path.
|
||||
storagePath := os.Getenv("PHOTOPRISM_STORAGE_PATH")
|
||||
@@ -57,7 +65,9 @@ func NewTestOptions(dbName string) *Options {
|
||||
return NewTestOptionsForPath(dbName, dataPath)
|
||||
}
|
||||
|
||||
// NewTestOptionsForPath returns new test Options using the specified data path as storage.
|
||||
// NewTestOptionsForPath returns test Options using the provided storage path.
|
||||
// When the caller omits the path, it falls back to storage/testdata, discovers
|
||||
// the repo-level assets directory, and ensures Hub traffic is disabled.
|
||||
func NewTestOptionsForPath(dbName, dataPath string) *Options {
|
||||
// Default to storage/testdata is no path was specified.
|
||||
if dataPath == "" {
|
||||
@@ -70,34 +80,49 @@ func NewTestOptionsForPath(dbName, dataPath string) *Options {
|
||||
dataPath = filepath.Join(storagePath, fs.TestdataDir)
|
||||
}
|
||||
|
||||
dataPath = fs.Abs(dataPath)
|
||||
// Enable test mode in dependencies.
|
||||
hub.ApplyTestConfig()
|
||||
|
||||
// Create specified data path as storage.
|
||||
dataPath = fs.Abs(dataPath)
|
||||
if err := fs.MkdirAll(dataPath); err != nil {
|
||||
log.Errorf("config: %s (create test data path)", err)
|
||||
return &Options{}
|
||||
}
|
||||
|
||||
// Create a config directory within the data path.
|
||||
configPath := filepath.Join(dataPath, "config")
|
||||
|
||||
if err := fs.MkdirAll(configPath); err != nil {
|
||||
log.Errorf("config: %s (create test config path)", err)
|
||||
return &Options{}
|
||||
}
|
||||
|
||||
// Find assets path.
|
||||
// Find the assets paths containing models and frontend assets.
|
||||
assetsPath := os.Getenv("PHOTOPRISM_ASSETS_PATH")
|
||||
if assetsPath == "" {
|
||||
fs.Abs("../../assets")
|
||||
if wd, err := os.Getwd(); err == nil {
|
||||
for dir := wd; dir != "" && dir != filepath.Dir(dir); dir = filepath.Dir(dir) {
|
||||
candidate := filepath.Join(dir, "assets")
|
||||
if fs.PathExists(candidate) {
|
||||
assetsPath = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if assetsPath == "" {
|
||||
assetsPath = fs.Abs("../../assets")
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain test database credentials.
|
||||
//
|
||||
// Example PHOTOPRISM_TEST_DSN for MariaDB / MySQL:
|
||||
// - "photoprism:photoprism@tcp(mariadb:4001)/photoprism?parseTime=true"
|
||||
dbName = PkgNameRegexp.ReplaceAllString(dbName, "")
|
||||
driver := os.Getenv("PHOTOPRISM_TEST_DRIVER")
|
||||
dsn := os.Getenv("PHOTOPRISM_TEST_DSN")
|
||||
|
||||
// Config example for MySQL / MariaDB:
|
||||
// driver = MySQL,
|
||||
// dsn = "photoprism:photoprism@tcp(mariadb:4001)/photoprism?parseTime=true",
|
||||
|
||||
// Set default test database driver.
|
||||
if driver == "test" || driver == "sqlite" || driver == "" || dsn == "" {
|
||||
driver = SQLite3
|
||||
@@ -183,6 +208,8 @@ func NewTestOptionsError() *Options {
|
||||
return c
|
||||
}
|
||||
|
||||
// SetNewTestConfig resets the singleton returned by TestConfig() so follow-up
|
||||
// calls build a fresh fixture-backed config instance.
|
||||
func SetNewTestConfig() {
|
||||
testConfig = NewTestConfig("test")
|
||||
}
|
||||
|
||||
@@ -161,8 +161,9 @@ func (c *Config) DecodeSession(cached bool) (Session, error) {
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
if _, err := hash.Write([]byte(c.Secret)); err != nil {
|
||||
return result, err
|
||||
|
||||
if _, hashErr := hash.Write([]byte(c.Secret)); hashErr != nil {
|
||||
return result, hashErr
|
||||
}
|
||||
|
||||
var b []byte
|
||||
@@ -182,8 +183,8 @@ func (c *Config) DecodeSession(cached bool) (Session, error) {
|
||||
|
||||
plaintext = bytes.Trim(plaintext, "\x00")
|
||||
|
||||
if err := json.Unmarshal(plaintext, &result); err != nil {
|
||||
return result, err
|
||||
if jsonErr := json.Unmarshal(plaintext, &result); jsonErr != nil {
|
||||
return result, jsonErr
|
||||
}
|
||||
|
||||
// Cache session.
|
||||
@@ -221,17 +222,23 @@ func (c *Config) ReSync(token string) (err error) {
|
||||
// interrupt reading of the Response.Body.
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
|
||||
endpointUrl := ServiceURL
|
||||
method := http.MethodPost
|
||||
endpointUrl := GetServiceURL(c.Key)
|
||||
|
||||
// Return if no endpoint URL is set.
|
||||
if endpointUrl == "" {
|
||||
log.Debugf("config: unable to obtain API key for maps and places (service disabled)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var method string
|
||||
var req *http.Request
|
||||
|
||||
if c.Key != "" {
|
||||
endpointUrl = fmt.Sprintf(ServiceURL+"/%s", c.Key)
|
||||
method = http.MethodPut
|
||||
log.Tracef("config: requesting updated keys for maps and places")
|
||||
if c.Key == "" {
|
||||
method = http.MethodPost
|
||||
log.Tracef("config: requesting new API key for maps and places")
|
||||
} else {
|
||||
log.Tracef("config: requesting new api keys for maps and places")
|
||||
method = http.MethodPut
|
||||
log.Tracef("config: requesting API key for maps and places")
|
||||
}
|
||||
|
||||
// Create JSON request.
|
||||
@@ -268,7 +275,7 @@ func (c *Config) ReSync(token string) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if r.StatusCode >= 400 {
|
||||
err = fmt.Errorf("fetching api key from %s failed (error %d)", ApiHost(), r.StatusCode)
|
||||
err = fmt.Errorf("requesting api key from %s failed (error %d)", GetServiceHost(), r.StatusCode)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ package hub
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// init lets debug builds override the Hub base URL via PHOTOPRISM_HUB_URL so
|
||||
// developers can point tests at staging services without code changes.
|
||||
func init() {
|
||||
if debugUrl := os.Getenv("PHOTOPRISM_HUB_URL"); debugUrl != "" {
|
||||
log.Infof("config: set hub url to %s", clean.Log(debugUrl))
|
||||
ServiceURL = debugUrl
|
||||
SetBaseURL(debugUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package hub
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
@@ -13,9 +14,6 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// FeedbackURL is the service endpoint for submitting user feedback.
|
||||
var FeedbackURL = ServiceURL + "/%s/feedback"
|
||||
|
||||
// Feedback represents user feedback submitted through the user interface.
|
||||
type Feedback struct {
|
||||
Category string `json:"Category"`
|
||||
@@ -67,12 +65,19 @@ func (c *Config) SendFeedback(frm form.Feedback) (err error) {
|
||||
// interrupt reading of the Response.Body.
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
|
||||
endpointUrl := fmt.Sprintf(FeedbackURL, c.Key)
|
||||
// Get feedback endpoint URL.
|
||||
endpointUrl := GetFeedbackServiceURL(c.Key)
|
||||
|
||||
// Return if no endpoint URL is set.
|
||||
if endpointUrl == "" {
|
||||
return errors.New("unable to send feedback (service disabled)")
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
|
||||
var req *http.Request
|
||||
|
||||
log.Debugf("sending feedback to %s", ApiHost())
|
||||
log.Debugf("config: sending feedback to %s", GetServiceHost())
|
||||
|
||||
if j, reqErr := json.Marshal(feedback); reqErr != nil {
|
||||
return reqErr
|
||||
@@ -103,7 +108,7 @@ func (c *Config) SendFeedback(frm form.Feedback) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if r.StatusCode >= 400 {
|
||||
err = fmt.Errorf("sending feedback to %s failed (error %d)", ApiHost(), r.StatusCode)
|
||||
err = fmt.Errorf("request to %s failed (error %d)", GetServiceHost(), r.StatusCode)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,17 @@ func TestSendFeedback(t *testing.T) {
|
||||
ClientCPU: 2,
|
||||
}
|
||||
|
||||
feedbackForm, err := form.NewFeedback(feedback)
|
||||
feedbackForm, formErr := form.NewFeedback(feedback)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if formErr != nil {
|
||||
t.Fatal(formErr)
|
||||
}
|
||||
|
||||
err2 := c.SendFeedback(feedbackForm)
|
||||
assert.Contains(t, err2.Error(), "failed")
|
||||
sendErr := c.SendFeedback(feedbackForm)
|
||||
assert.Error(t, sendErr)
|
||||
|
||||
if Disabled() {
|
||||
assert.EqualError(t, sendErr, "unable to send feedback (service disabled)")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestMain(m *testing.M) {
|
||||
log = logrus.StandardLogger()
|
||||
log.SetLevel(logrus.TraceLevel)
|
||||
|
||||
ServiceURL = "https://hub-int.photoprism.app/v1/hello"
|
||||
ApplyTestConfig()
|
||||
|
||||
code := m.Run()
|
||||
|
||||
@@ -78,6 +78,11 @@ func TestConfig_Refresh(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Skip assertions if disabled.
|
||||
if Disabled() {
|
||||
return
|
||||
}
|
||||
|
||||
assert.Len(t, c.Key, 40)
|
||||
assert.Len(t, c.Secret, 32)
|
||||
assert.Equal(t, "test", c.Version)
|
||||
@@ -111,7 +116,7 @@ func TestConfig_Refresh(t *testing.T) {
|
||||
} else if sess.Expired() {
|
||||
t.Fatal("session expired")
|
||||
} else {
|
||||
t.Logf("(2) session: %#v", sess)
|
||||
t.Logf("session: %#v", sess)
|
||||
}
|
||||
|
||||
if err := c.Save(); err != nil {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// ServiceURL specifies the service endpoint URL.
|
||||
var ServiceURL = "https://my.photoprism.app/v1/hello"
|
||||
|
||||
// Request represents basic environment specs for debugging.
|
||||
type Request struct {
|
||||
ClientVersion string `json:"ClientVersion"`
|
||||
@@ -21,7 +17,8 @@ type Request struct {
|
||||
ApiToken string `json:"ApiToken"`
|
||||
}
|
||||
|
||||
// ClientOpt returns a custom request option.
|
||||
// ClientOpt hooks let tests and extensions append optional context information
|
||||
// to Hub requests; callers may replace the function to emit custom strings.
|
||||
var ClientOpt = func() string {
|
||||
return ""
|
||||
}
|
||||
@@ -40,15 +37,3 @@ func NewRequest(version, serial, env, partnerId, token string) *Request {
|
||||
ApiToken: token,
|
||||
}
|
||||
}
|
||||
|
||||
// ApiHost returns the backend host name.
|
||||
func ApiHost() string {
|
||||
u, err := url.Parse(ServiceURL)
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return u.Host
|
||||
}
|
||||
|
||||
127
internal/service/hub/service.go
Normal file
127
internal/service/hub/service.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// Default service base URLs for testing and production.
|
||||
const (
|
||||
ProdBaseURL = "https://my.photoprism.app/v1/hello"
|
||||
TestBaseURL = "https://hub-int.photoprism.app/v1/hello"
|
||||
)
|
||||
|
||||
// baseURL specifies the service endpoint URL.
|
||||
var baseURL = ProdBaseURL
|
||||
|
||||
// GetServiceURL returns the currently configured Hub endpoint, optionally
|
||||
// appending the provided API key. An empty string is returned when Hub
|
||||
// requests are disabled.
|
||||
func GetServiceURL(key string) string {
|
||||
if baseURL == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return baseURL
|
||||
}
|
||||
|
||||
return fmt.Sprintf(baseURL+"/%s", key)
|
||||
}
|
||||
|
||||
// GetFeedbackServiceURL builds the feedback endpoint corresponding to the
|
||||
// supplied API key. A disabled Hub service results in an empty string.
|
||||
func GetFeedbackServiceURL(key string) string {
|
||||
if key == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
u := GetServiceURL(key)
|
||||
|
||||
if u == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return u + "/feedback"
|
||||
}
|
||||
|
||||
// GetServiceHost extracts the Hub host name from the active base URL, or
|
||||
// returns an empty string when the service is disabled or invalid.
|
||||
func GetServiceHost() string {
|
||||
s := GetServiceURL("")
|
||||
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return u.Host
|
||||
}
|
||||
|
||||
// SetBaseURL updates the Hub endpoint, ignoring inputs that are not HTTPS or
|
||||
// identical to the current value. Changes are logged so integration tests and
|
||||
// developers can trace the active target.
|
||||
func SetBaseURL(u string) {
|
||||
// Return if it is not an HTTPS URL.
|
||||
if !strings.HasPrefix(u, "https://") {
|
||||
return
|
||||
}
|
||||
|
||||
// Return if URL has not changed.
|
||||
if u == baseURL {
|
||||
return
|
||||
}
|
||||
|
||||
// Set new service endpoint URL.
|
||||
switch u {
|
||||
case TestBaseURL:
|
||||
log.Debug("config: enabled hub test service endpoint")
|
||||
case ProdBaseURL:
|
||||
log.Debug("config: enabled hub production service endpoint")
|
||||
default:
|
||||
log.Debugf("config: changed hub service endpoint to %s", clean.Log(u))
|
||||
}
|
||||
|
||||
baseURL = u
|
||||
}
|
||||
|
||||
// Disabled reports whether outbound Hub requests have been switched off.
|
||||
func Disabled() bool {
|
||||
return baseURL == ""
|
||||
}
|
||||
|
||||
// Disable clears the Hub endpoint so no network calls are attempted.
|
||||
func Disable() {
|
||||
// Return if already disabled.
|
||||
if Disabled() {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove configured endpoint URL to disable service.
|
||||
baseURL = ""
|
||||
log.Debugf("config: disabled hub service requests")
|
||||
}
|
||||
|
||||
// ApplyTestConfig reads PHOTOPRISM_TEST_HUB and switches the Hub endpoint to a
|
||||
// matching environment ("test", "prod"), disabling requests by default so
|
||||
// automated tests stay hermetic.
|
||||
func ApplyTestConfig() {
|
||||
switch os.Getenv("PHOTOPRISM_TEST_HUB") {
|
||||
case "true", "test", "int":
|
||||
SetBaseURL(TestBaseURL)
|
||||
case "prod":
|
||||
SetBaseURL(ProdBaseURL)
|
||||
default:
|
||||
Disable()
|
||||
}
|
||||
}
|
||||
114
internal/service/hub/service_test.go
Normal file
114
internal/service/hub/service_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func restoreBaseURL(t *testing.T) func() {
|
||||
t.Helper()
|
||||
|
||||
previous := GetServiceURL("")
|
||||
wasDisabled := Disabled()
|
||||
|
||||
return func() {
|
||||
if wasDisabled {
|
||||
Disable()
|
||||
return
|
||||
}
|
||||
|
||||
SetBaseURL(previous)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServiceURL(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
SetBaseURL(ProdBaseURL)
|
||||
|
||||
assert.Equal(t, ProdBaseURL, GetServiceURL(""))
|
||||
assert.Equal(t, ProdBaseURL+"/demo", GetServiceURL("demo"))
|
||||
|
||||
Disable()
|
||||
|
||||
assert.Empty(t, GetServiceURL("demo"))
|
||||
}
|
||||
|
||||
func TestGetFeedbackServiceURL(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
SetBaseURL(ProdBaseURL)
|
||||
|
||||
assert.Empty(t, GetFeedbackServiceURL(""))
|
||||
assert.Equal(t, ProdBaseURL+"/demo/feedback", GetFeedbackServiceURL("demo"))
|
||||
|
||||
Disable()
|
||||
|
||||
assert.Empty(t, GetFeedbackServiceURL("demo"))
|
||||
}
|
||||
|
||||
func TestGetServiceHost(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
SetBaseURL(ProdBaseURL)
|
||||
|
||||
assert.Equal(t, "my.photoprism.app", GetServiceHost())
|
||||
|
||||
Disable()
|
||||
|
||||
assert.Empty(t, GetServiceHost())
|
||||
}
|
||||
|
||||
func TestSetBaseURLRejectsHTTP(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
SetBaseURL(ProdBaseURL)
|
||||
SetBaseURL("http://example.com/v1/hello")
|
||||
|
||||
assert.Equal(t, ProdBaseURL, GetServiceURL(""))
|
||||
}
|
||||
|
||||
func TestApplyTestConfig(t *testing.T) {
|
||||
t.Run("DisableByDefault", func(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
t.Setenv("PHOTOPRISM_TEST_HUB", "")
|
||||
SetBaseURL(ProdBaseURL)
|
||||
|
||||
ApplyTestConfig()
|
||||
|
||||
assert.True(t, Disabled())
|
||||
})
|
||||
|
||||
t.Run("EnableTest", func(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
t.Setenv("PHOTOPRISM_TEST_HUB", "test")
|
||||
Disable()
|
||||
|
||||
ApplyTestConfig()
|
||||
|
||||
assert.False(t, Disabled())
|
||||
assert.Equal(t, TestBaseURL, GetServiceURL(""))
|
||||
})
|
||||
|
||||
t.Run("EnableProd", func(t *testing.T) {
|
||||
cleanup := restoreBaseURL(t)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
t.Setenv("PHOTOPRISM_TEST_HUB", "prod")
|
||||
Disable()
|
||||
|
||||
ApplyTestConfig()
|
||||
|
||||
assert.False(t, Disabled())
|
||||
assert.Equal(t, ProdBaseURL, GetServiceURL(""))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user