Add AppAPI Notifications and Users and Groups reads.
ExApps can Send/SendTo a bell for one Recipient and read group membership and directory OCS as the Requesting user, with Access Gate using UserGroups. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
package gonexapp_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.neitzel.de/konrad/go-nc-exapp"
|
||||
)
|
||||
|
||||
func authUserFromRequest(r *http.Request) string {
|
||||
raw := r.Header.Get("AUTHORIZATION-APP-API")
|
||||
decoded, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
parts := strings.SplitN(string(decoded), ":", 2)
|
||||
if len(parts) < 1 {
|
||||
return ""
|
||||
}
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendPostsForCredentialsUser(t *testing.T) {
|
||||
var (
|
||||
gotMethod string
|
||||
gotPath string
|
||||
gotUser string
|
||||
gotBody []byte
|
||||
)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotMethod = r.Method
|
||||
gotPath = r.URL.Path
|
||||
gotUser = authUserFromRequest(r)
|
||||
gotBody, _ = io.ReadAll(r.Body)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
err := api.Send(gonexapp.Notification{Subject: "Hello"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gotMethod != http.MethodPost {
|
||||
t.Fatalf("method=%q", gotMethod)
|
||||
}
|
||||
if !strings.Contains(gotPath, "apps/app_api/api/v1/notification") {
|
||||
t.Fatalf("path=%q", gotPath)
|
||||
}
|
||||
if gotUser != "alice" {
|
||||
t.Fatalf("auth user=%q", gotUser)
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Params struct {
|
||||
Object string `json:"object"`
|
||||
ObjectID string `json:"object_id"`
|
||||
SubjectType string `json:"subject_type"`
|
||||
SubjectParams struct {
|
||||
RichSubject string `json:"rich_subject"`
|
||||
RichSubjectParams map[string]any `json:"rich_subject_params"`
|
||||
} `json:"subject_params"`
|
||||
} `json:"params"`
|
||||
}
|
||||
if err := json.Unmarshal(gotBody, &payload); err != nil {
|
||||
t.Fatalf("body: %v\n%s", err, gotBody)
|
||||
}
|
||||
if payload.Params.Object != "app_api" {
|
||||
t.Fatalf("object=%q", payload.Params.Object)
|
||||
}
|
||||
if payload.Params.ObjectID != "app_api_id" {
|
||||
t.Fatalf("object_id=%q", payload.Params.ObjectID)
|
||||
}
|
||||
if payload.Params.SubjectType != "app_api_ex_app" {
|
||||
t.Fatalf("subject_type=%q", payload.Params.SubjectType)
|
||||
}
|
||||
if payload.Params.SubjectParams.RichSubject != "Hello" {
|
||||
t.Fatalf("rich_subject=%q", payload.Params.SubjectParams.RichSubject)
|
||||
}
|
||||
if payload.Params.SubjectParams.RichSubjectParams == nil {
|
||||
t.Fatal("rich_subject_params is null")
|
||||
}
|
||||
if len(payload.Params.SubjectParams.RichSubjectParams) != 0 {
|
||||
t.Fatalf("rich_subject_params=%#v", payload.Params.SubjectParams.RichSubjectParams)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendToImpersonatesGivenUser(t *testing.T) {
|
||||
var gotUser string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotUser = authUserFromRequest(r)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
err := api.SendTo("bob", gonexapp.Notification{Subject: "Hello"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gotUser != "bob" {
|
||||
t.Fatalf("auth user=%q, want bob", gotUser)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsRejectsEmptySubject(t *testing.T) {
|
||||
called := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
if err := api.Send(gonexapp.Notification{}); err == nil {
|
||||
t.Fatal("Send: expected error for empty Subject")
|
||||
}
|
||||
if err := api.SendTo("bob", gonexapp.Notification{Message: "no subject"}); err == nil {
|
||||
t.Fatal("SendTo: expected error for empty Subject")
|
||||
}
|
||||
if called {
|
||||
t.Fatal("OCS was called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendRejectsEmptyUserID(t *testing.T) {
|
||||
called := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
if err := api.Send(gonexapp.Notification{Subject: "Hello"}); err == nil {
|
||||
t.Fatal("expected error for empty UserID")
|
||||
}
|
||||
if called {
|
||||
t.Fatal("OCS was called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendToRejectsEmptyUserID(t *testing.T) {
|
||||
called := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
if err := api.SendTo("", gonexapp.Notification{Subject: "Hello"}); err == nil {
|
||||
t.Fatal("expected error for empty userID")
|
||||
}
|
||||
if called {
|
||||
t.Fatal("OCS was called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendIncludesOptionalMessageAndLink(t *testing.T) {
|
||||
var gotBody []byte
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotBody, _ = io.ReadAll(r.Body)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ocs": map[string]any{"data": map[string]any{}}})
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
err := api.Send(gonexapp.Notification{
|
||||
Subject: "Hello",
|
||||
Message: "Details here",
|
||||
Link: "https://cloud.example/apps/checkdns",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Params struct {
|
||||
SubjectParams struct {
|
||||
RichMessage string `json:"rich_message"`
|
||||
RichMessageParams map[string]any `json:"rich_message_params"`
|
||||
Link string `json:"link"`
|
||||
} `json:"subject_params"`
|
||||
} `json:"params"`
|
||||
}
|
||||
if err := json.Unmarshal(gotBody, &payload); err != nil {
|
||||
t.Fatalf("body: %v\n%s", err, gotBody)
|
||||
}
|
||||
if payload.Params.SubjectParams.RichMessage != "Details here" {
|
||||
t.Fatalf("rich_message=%q", payload.Params.SubjectParams.RichMessage)
|
||||
}
|
||||
if payload.Params.SubjectParams.RichMessageParams == nil {
|
||||
t.Fatal("rich_message_params is null")
|
||||
}
|
||||
if payload.Params.SubjectParams.Link != "https://cloud.example/apps/checkdns" {
|
||||
t.Fatalf("link=%q", payload.Params.SubjectParams.Link)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppAPINotificationsSendSurfacesOCSStatus(t *testing.T) {
|
||||
for _, code := range []int{http.StatusBadRequest, http.StatusForbidden} {
|
||||
t.Run(http.StatusText(code), func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "nope", code)
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
cred := gonexapp.Credentials{
|
||||
BaseURL: srv.URL, AppID: "app", AppVersion: "0.1.0", AAVersion: "1.0.0", AppSecret: "s", UserID: "alice",
|
||||
}
|
||||
api := gonexapp.NewAppAPINotifications(cred)
|
||||
api.Client = srv.Client()
|
||||
api.OCS.Client = srv.Client()
|
||||
|
||||
err := api.Send(gonexapp.Notification{Subject: "Hello"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
want := fmt.Sprintf("%d", code)
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("error %q missing status %s", err, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user