forked from baron/baron-sso
- Added support for fixed UUIDs during bulk registration (Search-first + ExternalID mapping) - Implemented idempotency and visibility restoration for soft-deleted users - Enhanced bulk upload UI to show 'New/Updated/Unchanged' status and modified fields - Added logic to reclaim identifiers (login_id) from colliding records - Added frontend E2E and backend unit tests for UUID integrity and conflict handling - Fixed i18n, formatting, and mock tests to satisfy code-check - Applied 'go fix' for 'omitzero' tags and general Go standards
157 lines
4.5 KiB
Go
157 lines
4.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestKetoService_CheckPermission(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/relation-tuples/check", r.URL.Path)
|
|
assert.Equal(t, "user1", r.URL.Query().Get("subject_id"))
|
|
assert.Equal(t, "tenants", r.URL.Query().Get("namespace"))
|
|
assert.Equal(t, "tenant1", r.URL.Query().Get("object"))
|
|
assert.Equal(t, "admin", r.URL.Query().Get("relation"))
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(checkResponse{Allowed: true})
|
|
})
|
|
|
|
s := &ketoService{
|
|
readURL: "http://keto-read.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
allowed, err := s.CheckPermission(context.Background(), "user1", "tenants", "tenant1", "admin")
|
|
assert.NoError(t, err)
|
|
assert.True(t, allowed)
|
|
}
|
|
|
|
func TestKetoService_CreateRelation(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/admin/relation-tuples", r.URL.Path)
|
|
assert.Equal(t, "PUT", r.Method)
|
|
|
|
var body map[string]any
|
|
_ = json.NewDecoder(r.Body).Decode(&body)
|
|
assert.Equal(t, "tenants", body["namespace"])
|
|
assert.Equal(t, "tenant1", body["object"])
|
|
assert.Equal(t, "admin", body["relation"])
|
|
assert.Equal(t, "user1", body["subject_id"])
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
})
|
|
|
|
s := &ketoService{
|
|
writeURL: "http://keto-write.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
err := s.CreateRelation(context.Background(), "tenants", "tenant1", "admin", "user1")
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestKetoService_DeleteRelation(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/admin/relation-tuples", r.URL.Path)
|
|
assert.Equal(t, "DELETE", r.Method)
|
|
assert.Equal(t, "user1", r.URL.Query().Get("subject_id"))
|
|
assert.Equal(t, "tenants", r.URL.Query().Get("namespace"))
|
|
assert.Equal(t, "tenant1", r.URL.Query().Get("object"))
|
|
assert.Equal(t, "admin", r.URL.Query().Get("relation"))
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
})
|
|
|
|
s := &ketoService{
|
|
writeURL: "http://keto-write.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
err := s.DeleteRelation(context.Background(), "tenants", "tenant1", "admin", "user1")
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestKetoService_ListRelations(t *testing.T) {
|
|
tuples := []RelationTuple{
|
|
{Namespace: "tenants", Object: "tenant1", Relation: "admin", SubjectID: "user1"},
|
|
}
|
|
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/relation-tuples", r.URL.Path)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(relationTuplesResponse{RelationTuples: tuples})
|
|
})
|
|
|
|
s := &ketoService{
|
|
readURL: "http://keto-read.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
result, err := s.ListRelations(context.Background(), "tenants", "tenant1", "admin", "user1")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tuples, result)
|
|
}
|
|
|
|
func TestKetoService_ErrorHandling(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte("internal error"))
|
|
})
|
|
|
|
s := &ketoService{
|
|
readURL: "http://keto-read.local",
|
|
writeURL: "http://keto-write.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
_, err := s.CheckPermission(context.Background(), "u", "n", "o", "r")
|
|
assert.Error(t, err)
|
|
|
|
err = s.DeleteRelation(context.Background(), "n", "o", "r", "s")
|
|
assert.Error(t, err)
|
|
|
|
_, err = s.ListRelations(context.Background(), "n", "o", "r", "s")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestKetoService_CheckPermission_Forbidden(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
})
|
|
|
|
s := &ketoService{
|
|
readURL: "http://keto-read.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
allowed, err := s.CheckPermission(context.Background(), "u", "n", "o", "r")
|
|
assert.NoError(t, err)
|
|
assert.False(t, allowed)
|
|
}
|
|
|
|
func TestKetoService_CreateRelation_Retry(t *testing.T) {
|
|
attempts := 0
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
attempts++
|
|
if attempts < 2 {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
})
|
|
|
|
s := &ketoService{
|
|
writeURL: "http://keto-write.local",
|
|
client: clientForHandler(handler),
|
|
}
|
|
|
|
err := s.CreateRelation(context.Background(), "n", "o", "r", "s")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, attempts)
|
|
}
|