forked from Gitlink/gitlink-cli
337 lines
11 KiB
Go
337 lines
11 KiB
Go
package messagesetting
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gitlink-org/gitlink-cli/internal/client"
|
|
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
|
|
)
|
|
|
|
func runMessageSettingShortcut(t *testing.T, server *httptest.Server, name string, args map[string]string) error {
|
|
t.Helper()
|
|
shortcut := findMessageSettingShortcut(t, name)
|
|
ctx := &common.RuntimeContext{
|
|
Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL},
|
|
Format: "json",
|
|
Args: args,
|
|
}
|
|
if ctx.Args == nil {
|
|
ctx.Args = map[string]string{}
|
|
}
|
|
return shortcut.Run(ctx)
|
|
}
|
|
|
|
func findMessageSettingShortcut(t *testing.T, name string) *common.Shortcut {
|
|
t.Helper()
|
|
for _, shortcut := range Shortcuts() {
|
|
if shortcut.Name == name {
|
|
return shortcut
|
|
}
|
|
}
|
|
t.Fatalf("shortcut %q not found", name)
|
|
return nil
|
|
}
|
|
|
|
func writeMessageSettingJSON(t *testing.T, w http.ResponseWriter, value interface{}) {
|
|
t.Helper()
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(value); err != nil {
|
|
t.Fatalf("encode json: %v", err)
|
|
}
|
|
}
|
|
|
|
func decodeMessageSettingJSON(t *testing.T, r *http.Request) map[string]interface{} {
|
|
t.Helper()
|
|
defer r.Body.Close()
|
|
var value map[string]interface{}
|
|
if err := json.NewDecoder(r.Body).Decode(&value); err != nil {
|
|
t.Fatalf("decode request body: %v", err)
|
|
}
|
|
return value
|
|
}
|
|
|
|
func TestMessageSettingsCatalog(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet || r.URL.Path != "/api/template_message_settings.json" {
|
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
}
|
|
writeMessageSettingJSON(t, w, catalogFixture())
|
|
}))
|
|
defer server.Close()
|
|
|
|
if err := runMessageSettingShortcut(t, server, "catalog", nil); err != nil {
|
|
t.Fatalf("catalog failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMessageSettingsViewUsesCurrentUserWhenLoginMissing(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/users/me.json":
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"login": "alice"})
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, catalogFixture())
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/users/alice/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, userSettingFixture())
|
|
default:
|
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
if err := runMessageSettingShortcut(t, server, "view", nil); err != nil {
|
|
t.Fatalf("view failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMessageSettingsUpdateDryRunDoesNotPost(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/users/me.json":
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"login": "alice"})
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, catalogFixture())
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/users/alice/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, userSettingFixture())
|
|
default:
|
|
t.Fatalf("dry-run should not write, got %s %s", r.Method, r.URL.Path)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
err := runMessageSettingShortcut(t, server, "update", map[string]string{
|
|
"channel": "notification",
|
|
"state": "off",
|
|
"keys": "Normal::Permission",
|
|
"dry-run": "true",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("update dry-run failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMessageSettingsUpdatePostsMergedPayload(t *testing.T) {
|
|
var payload map[string]interface{}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/users/me.json":
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"login": "alice"})
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, catalogFixture())
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/users/alice/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, userSettingFixture())
|
|
case r.Method == http.MethodPost && r.URL.Path == "/api/users/alice/template_message_settings/update_setting.json":
|
|
payload = decodeMessageSettingJSON(t, r)
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"status": 0, "message": "updated"})
|
|
default:
|
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
err := runMessageSettingShortcut(t, server, "update", map[string]string{
|
|
"channel": "email",
|
|
"state": "on",
|
|
"group": "ManageProject",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("update failed: %v", err)
|
|
}
|
|
|
|
setting, ok := payload["setting"].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("missing setting payload: %#v", payload)
|
|
}
|
|
notificationBody := setting["notification_body"].(map[string]interface{})
|
|
emailBody := setting["email_body"].(map[string]interface{})
|
|
|
|
if notificationBody["ManageProject::Issue"] != true {
|
|
t.Fatalf("notification should stay true for ManageProject::Issue, got %#v", notificationBody["ManageProject::Issue"])
|
|
}
|
|
if emailBody["ManageProject::Issue"] != true {
|
|
t.Fatalf("email should be enabled for ManageProject::Issue, got %#v", emailBody["ManageProject::Issue"])
|
|
}
|
|
if emailBody["Normal::Permission"] != false {
|
|
t.Fatalf("email should preserve unrelated keys, got %#v", emailBody["Normal::Permission"])
|
|
}
|
|
if notificationBody["ManageProject::Praised"] != true {
|
|
t.Fatalf("notification defaults should be preserved for missing keys, got %#v", notificationBody["ManageProject::Praised"])
|
|
}
|
|
if emailBody["ManageProject::Praised"] != true {
|
|
t.Fatalf("selected group keys should be updated even when missing from current settings, got %#v", emailBody["ManageProject::Praised"])
|
|
}
|
|
}
|
|
|
|
func TestMessageSettingsPresetPostsSelectedKeys(t *testing.T) {
|
|
var payload map[string]interface{}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/users/me.json":
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"login": "alice"})
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, catalogFixture())
|
|
case r.Method == http.MethodGet && r.URL.Path == "/api/users/alice/template_message_settings.json":
|
|
writeMessageSettingJSON(t, w, userSettingFixture())
|
|
case r.Method == http.MethodPost && r.URL.Path == "/api/users/alice/template_message_settings/update_setting.json":
|
|
payload = decodeMessageSettingJSON(t, r)
|
|
writeMessageSettingJSON(t, w, map[string]interface{}{"status": 0, "message": "updated"})
|
|
default:
|
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
err := runMessageSettingShortcut(t, server, "preset", map[string]string{
|
|
"name": "all-off",
|
|
"keys": "Permission,ManageProject::Issue",
|
|
"all": "false",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("preset failed: %v", err)
|
|
}
|
|
|
|
setting := payload["setting"].(map[string]interface{})
|
|
notificationBody := setting["notification_body"].(map[string]interface{})
|
|
emailBody := setting["email_body"].(map[string]interface{})
|
|
|
|
if notificationBody["Normal::Permission"] != false || emailBody["Normal::Permission"] != false {
|
|
t.Fatalf("Normal::Permission should be turned off by preset")
|
|
}
|
|
if notificationBody["ManageProject::Issue"] != false || emailBody["ManageProject::Issue"] != false {
|
|
t.Fatalf("ManageProject::Issue should be turned off by preset")
|
|
}
|
|
if notificationBody["ManageProject::PullRequest"] != true {
|
|
t.Fatalf("unselected keys should remain unchanged, got %#v", notificationBody["ManageProject::PullRequest"])
|
|
}
|
|
}
|
|
|
|
func TestSelectKeysRequiresSelector(t *testing.T) {
|
|
registry := buildRegistry(mustCatalogResponse(t, catalogFixture()), mustUserSettingResponse(t, userSettingFixture()))
|
|
if _, _, err := selectKeys(selectorArgs{}, registry); err == nil {
|
|
t.Fatal("expected selector validation error")
|
|
}
|
|
}
|
|
|
|
func TestParseGroupFilterRejectsUnknownGroup(t *testing.T) {
|
|
registry := buildRegistry(mustCatalogResponse(t, catalogFixture()), nil)
|
|
if _, err := parseGroupFilter("UnknownGroup", registry); err == nil {
|
|
t.Fatal("expected unknown group error")
|
|
}
|
|
}
|
|
|
|
func TestResolveKeyFilterRejectsAmbiguousShortKey(t *testing.T) {
|
|
catalog := &catalogResponse{
|
|
SettingTypes: []catalogGroup{
|
|
{
|
|
Type: "TemplateMessageSetting::Normal",
|
|
TypeName: "Normal",
|
|
Settings: []catalogSettingRow{
|
|
{Name: "Issue", Key: "Issue"},
|
|
},
|
|
},
|
|
{
|
|
Type: "TemplateMessageSetting::ManageProject",
|
|
TypeName: "Manage",
|
|
Settings: []catalogSettingRow{
|
|
{Name: "Issue", Key: "Issue"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
registry := buildRegistry(catalog, nil)
|
|
if _, err := resolveKeyFilter("Issue", registry); err == nil {
|
|
t.Fatal("expected ambiguous key error")
|
|
}
|
|
}
|
|
|
|
func catalogFixture() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"setting_types": []map[string]interface{}{
|
|
{
|
|
"type": "TemplateMessageSetting::Normal",
|
|
"type_name": "My status",
|
|
"settings": []map[string]interface{}{
|
|
{
|
|
"name": "Permission changed",
|
|
"key": "Permission",
|
|
"notification_disabled": false,
|
|
"email_disabled": false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"type": "TemplateMessageSetting::ManageProject",
|
|
"type_name": "Managed repositories",
|
|
"settings": []map[string]interface{}{
|
|
{
|
|
"name": "New issue",
|
|
"key": "Issue",
|
|
"notification_disabled": false,
|
|
"email_disabled": false,
|
|
},
|
|
{
|
|
"name": "New pull request",
|
|
"key": "PullRequest",
|
|
"notification_disabled": false,
|
|
"email_disabled": false,
|
|
},
|
|
{
|
|
"name": "Praised",
|
|
"key": "Praised",
|
|
"notification_disabled": false,
|
|
"email_disabled": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func userSettingFixture() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"user": map[string]interface{}{
|
|
"login": "alice",
|
|
"name": "Alice",
|
|
},
|
|
"notification_body": map[string]interface{}{
|
|
"Normal::Permission": true,
|
|
"ManageProject::Issue": true,
|
|
"ManageProject::PullRequest": true,
|
|
},
|
|
"email_body": map[string]interface{}{
|
|
"Normal::Permission": false,
|
|
"ManageProject::Issue": false,
|
|
"ManageProject::PullRequest": false,
|
|
},
|
|
}
|
|
}
|
|
|
|
func mustCatalogResponse(t *testing.T, value map[string]interface{}) *catalogResponse {
|
|
t.Helper()
|
|
raw, err := json.Marshal(value)
|
|
if err != nil {
|
|
t.Fatalf("marshal catalog fixture: %v", err)
|
|
}
|
|
var result catalogResponse
|
|
if err := json.Unmarshal(raw, &result); err != nil {
|
|
t.Fatalf("unmarshal catalog fixture: %v", err)
|
|
}
|
|
return &result
|
|
}
|
|
|
|
func mustUserSettingResponse(t *testing.T, value map[string]interface{}) *userSettingResponse {
|
|
t.Helper()
|
|
raw, err := json.Marshal(value)
|
|
if err != nil {
|
|
t.Fatalf("marshal user setting fixture: %v", err)
|
|
}
|
|
var result userSettingResponse
|
|
if err := json.Unmarshal(raw, &result); err != nil {
|
|
t.Fatalf("unmarshal user setting fixture: %v", err)
|
|
}
|
|
return &result
|
|
}
|