mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-26 21:01:58 +08:00
102 lines
3.9 KiB
Go
102 lines
3.9 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/photoprism/photoprism/internal/service/cluster"
|
|
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
|
)
|
|
|
|
func TestClusterNodesRegister(t *testing.T) {
|
|
t.Run("FeatureDisabled", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RoleInstance
|
|
ClusterNodesRegister(router)
|
|
|
|
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":"pp-node-01"}`)
|
|
assert.Equal(t, http.StatusForbidden, r.Code)
|
|
})
|
|
|
|
t.Run("MissingToken", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RolePortal
|
|
ClusterNodesRegister(router)
|
|
|
|
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":"pp-node-01"}`)
|
|
assert.Equal(t, http.StatusUnauthorized, r.Code)
|
|
})
|
|
|
|
t.Run("DriverConflict", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RolePortal
|
|
conf.Options().JoinToken = "t0k3n"
|
|
ClusterNodesRegister(router)
|
|
|
|
// With SQLite driver in tests, provisioning should fail with conflict.
|
|
r := AuthenticatedRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":"pp-node-01"}`, "t0k3n")
|
|
assert.Equal(t, http.StatusConflict, r.Code)
|
|
assert.Contains(t, r.Body.String(), "portal database must be MySQL/MariaDB")
|
|
})
|
|
|
|
t.Run("BadName", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RolePortal
|
|
conf.Options().JoinToken = "t0k3n"
|
|
ClusterNodesRegister(router)
|
|
|
|
// Empty nodeName → 400
|
|
r := AuthenticatedRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":""}`, "t0k3n")
|
|
assert.Equal(t, http.StatusBadRequest, r.Code)
|
|
})
|
|
|
|
t.Run("RotateSecretPersistsDespiteDBConflict", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RolePortal
|
|
conf.Options().JoinToken = "t0k3n"
|
|
ClusterNodesRegister(router)
|
|
|
|
// Pre-create node in registry so handler goes through existing-node path
|
|
// and rotates the secret before attempting DB ensure.
|
|
regy, err := reg.NewClientRegistryWithConfig(conf)
|
|
assert.NoError(t, err)
|
|
n := ®.Node{ID: "test-id", Name: "pp-node-01", Role: "instance"}
|
|
assert.NoError(t, regy.Put(n))
|
|
|
|
r := AuthenticatedRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":"pp-node-01","rotateSecret":true}`, "t0k3n")
|
|
assert.Equal(t, http.StatusConflict, r.Code) // DB conflict under SQLite
|
|
|
|
// Secret should have rotated and been persisted even though DB ensure failed.
|
|
// Fetch by name (most-recently-updated) to avoid flakiness if another test adds
|
|
// a node with the same name and a different id.
|
|
n2, err := regy.FindByName("pp-node-01")
|
|
assert.NoError(t, err)
|
|
// With client-backed registry, plaintext secret is not persisted; only rotation timestamp is updated.
|
|
assert.NotEmpty(t, n2.SecretRot)
|
|
})
|
|
|
|
t.Run("ExistingNodeSiteUrlPersistsEvenOnDBConflict", func(t *testing.T) {
|
|
app, router, conf := NewApiTest()
|
|
conf.Options().NodeRole = cluster.RolePortal
|
|
conf.Options().JoinToken = "t0k3n"
|
|
ClusterNodesRegister(router)
|
|
|
|
// Pre-create node in registry so handler goes through existing-node path.
|
|
regy, err := reg.NewClientRegistryWithConfig(conf)
|
|
assert.NoError(t, err)
|
|
n := ®.Node{Name: "pp-node-02", Role: "instance"}
|
|
assert.NoError(t, regy.Put(n))
|
|
|
|
// With SQLite driver in tests, provisioning should fail with 409, but metadata should still persist.
|
|
r := AuthenticatedRequestWithBody(app, http.MethodPost, "/api/v1/cluster/nodes/register", `{"nodeName":"pp-node-02","siteUrl":"https://Photos.Example.COM"}`, "t0k3n")
|
|
assert.Equal(t, http.StatusConflict, r.Code)
|
|
|
|
// Ensure normalized/persisted siteUrl.
|
|
n2, err := regy.FindByName("pp-node-02")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "https://photos.example.com", n2.SiteUrl)
|
|
})
|
|
}
|