Files
larksuite--cli/shortcuts/apps/apps_openapi_key_common_test.go
wehub-resource-sync bf9395e022
CI / results (push) Blocked by required conditions
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / e2e-live (push) Waiting to run
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / deadcode (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

357 lines
12 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package apps
import (
"reflect"
"testing"
)
func TestMaskAPIKey(t *testing.T) {
cases := map[string]string{
"": "****",
"abcd": "****",
"xxxxxxxxxxxx": "****xxxx",
}
for in, want := range cases {
if got := maskAPIKey(in); got != want {
t.Errorf("maskAPIKey(%q) = %q, want %q", in, got, want)
}
}
}
func TestRedactKeyInfo_StripsRawKey(t *testing.T) {
in := map[string]interface{}{
"api_key_id": "k1",
"api_key": "xxxxxxxxxxxx",
"name": "partner-test",
"status": float64(1),
}
out := redactKeyInfo(in)
if _, ok := out["api_key"]; ok {
t.Fatalf("redactKeyInfo must strip api_key, got %v", out)
}
if out["key_preview"] != "****xxxx" {
t.Errorf("key_preview = %v, want ****xxxx", out["key_preview"])
}
if out["name"] != "partner-test" || out["api_key_id"] != "k1" {
t.Errorf("non-secret fields must be preserved, got %v", out)
}
// input not mutated
if _, ok := in["api_key"]; !ok {
t.Errorf("redactKeyInfo must not mutate input")
}
}
func TestParseScopeAPI(t *testing.T) {
t.Run("valid", func(t *testing.T) {
info, err := parseScopeAPI("GET /openapi/v1/orders")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if info["http_method"] != "GET" {
t.Errorf("http_method = %v, want GET", info["http_method"])
}
if info["http_path"] != "/openapi/v1/orders" {
t.Errorf("http_path = %v, want /openapi/v1/orders", info["http_path"])
}
})
t.Run("lowercase method uppercased", func(t *testing.T) {
info, err := parseScopeAPI("post /openapi/x")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if info["http_method"] != "POST" {
t.Errorf("http_method = %v, want POST", info["http_method"])
}
})
t.Run("too few fields", func(t *testing.T) {
if _, err := parseScopeAPI("GET"); err == nil {
t.Errorf("one-word input must error")
}
})
t.Run("too many fields", func(t *testing.T) {
if _, err := parseScopeAPI("GET /openapi/x extra"); err == nil {
t.Errorf("three-word input must error")
}
})
}
func TestValidateScopeAPIMethod(t *testing.T) {
for _, m := range []string{"GET", "POST", "PUT", "PATCH", "DELETE"} {
if err := validateScopeAPIMethod(m); err != nil {
t.Errorf("validateScopeAPIMethod(%q) = %v, want nil", m, err)
}
}
for _, m := range []string{"TRACE", "CONNECT", "OPTIONS", "HEAD", "", "get"} {
if err := validateScopeAPIMethod(m); err == nil {
t.Errorf("validateScopeAPIMethod(%q) = nil, want error", m)
}
}
}
func TestValidateScopeAPIPath(t *testing.T) {
for _, p := range []string{"/openapi/orders", "/openapi/v1/x"} {
if err := validateScopeAPIPath(p); err != nil {
t.Errorf("validateScopeAPIPath(%q) = %v, want nil", p, err)
}
}
for _, p := range []string{"", "openapi/x", "/openapi/../admin", "/..", "/openapi//x", "//x"} {
if err := validateScopeAPIPath(p); err == nil {
t.Errorf("validateScopeAPIPath(%q) = nil, want error", p)
}
}
}
func TestValidateRequestScopeFields(t *testing.T) {
ok := []map[string]interface{}{
{"allow_all": true},
{"allow_all": false, "http_infos": []interface{}{
map[string]interface{}{"http_method": "GET", "http_path": "/openapi/x"},
}},
{},
}
for _, rs := range ok {
if err := validateRequestScopeFields(rs); err != nil {
t.Errorf("validateRequestScopeFields(%v) = %v, want nil", rs, err)
}
}
bad := []map[string]interface{}{
{"foo": 1}, // unknown top-level field
{"allow_all": "yes"}, // wrong type
{"http_infos": "x"}, // not an array
{"http_infos": []interface{}{"x"}}, // entry not an object
{"http_infos": []interface{}{map[string]interface{}{"http_method": "TRACE", "http_path": "/x"}}}, // bad method
{"http_infos": []interface{}{map[string]interface{}{"http_method": "GET", "http_path": "../x"}}}, // bad path
{"http_infos": []interface{}{map[string]interface{}{"http_method": "GET", "http_path": "/x", "extra": 1}}}, // unknown entry field
}
for _, rs := range bad {
if err := validateRequestScopeFields(rs); err == nil {
t.Errorf("validateRequestScopeFields(%v) = nil, want error", rs)
}
}
}
func TestParseRawScope(t *testing.T) {
if _, err := parseRawScope(`{"allow_all":true}`); err != nil {
t.Errorf("valid object errored: %v", err)
}
for _, raw := range []string{`["x"]`, `"s"`, `123`, `{"foo":1}`, `{bad`} {
if _, err := parseRawScope(raw); err == nil {
t.Errorf("parseRawScope(%q) = nil, want error", raw)
}
}
}
func TestParseScopeAPI_Rejects(t *testing.T) {
bad := []string{"TRACE /openapi/x", "CONNECT /x", "GET ../admin", "GET openapi/x", "GET /a//b"}
for _, in := range bad {
if _, err := parseScopeAPI(in); err == nil {
t.Errorf("parseScopeAPI(%q) = nil, want error", in)
}
}
// regression: legitimate input still parses (and lowercases the method)
info, err := parseScopeAPI("get /openapi/orders")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if info["http_method"] != "GET" || info["http_path"] != "/openapi/orders" {
t.Errorf("info = %v", info)
}
}
func TestBuildRequestScope_RawValidation(t *testing.T) {
// unknown field now rejected (HIGH-2)
if _, err := buildRequestScope(false, nil, `{"foo":1}`); err == nil {
t.Errorf("raw scope with unknown field must error")
}
// non-object rejected
if _, err := buildRequestScope(false, nil, `["x"]`); err == nil {
t.Errorf("non-object raw scope must error")
}
// nested bad method rejected
if _, err := buildRequestScope(false, nil, `{"http_infos":[{"http_method":"TRACE","http_path":"/x"}]}`); err == nil {
t.Errorf("raw scope with bad nested method must error")
}
// regression: documented fields pass
if _, err := buildRequestScope(false, nil, `{"allow_all":true}`); err != nil {
t.Errorf("valid raw scope errored: %v", err)
}
}
func TestBuildRequestScope(t *testing.T) {
t.Run("nothing set -> nil", func(t *testing.T) {
rs, err := buildRequestScope(false, nil, "")
if err != nil || rs != nil {
t.Fatalf("expected nil,nil got rs=%v err=%v", rs, err)
}
})
t.Run("scope-all only", func(t *testing.T) {
rs, err := buildRequestScope(true, nil, "")
if err != nil {
t.Fatalf("err = %v", err)
}
m := rs.(map[string]interface{})
if m["allow_all"] != true {
t.Errorf("allow_all = %v, want true", m["allow_all"])
}
if _, ok := m["http_infos"]; ok {
t.Errorf("http_infos should not appear when no scope-api provided")
}
})
t.Run("scope-api adds http_infos", func(t *testing.T) {
rs, err := buildRequestScope(false, []string{"GET /openapi/x"}, "")
if err != nil {
t.Fatalf("err = %v", err)
}
m := rs.(map[string]interface{})
if m["allow_all"] != false {
t.Errorf("allow_all = %v, want false", m["allow_all"])
}
infos := m["http_infos"].([]interface{})
if len(infos) != 1 {
t.Fatalf("http_infos len = %d, want 1", len(infos))
}
info := infos[0].(map[string]interface{})
if info["http_method"] != "GET" || info["http_path"] != "/openapi/x" {
t.Errorf("info = %v", info)
}
})
t.Run("raw scope passthrough", func(t *testing.T) {
rs, err := buildRequestScope(false, nil, `{"allow_all":true}`)
if err != nil {
t.Fatalf("err = %v", err)
}
m := rs.(map[string]interface{})
if m["allow_all"] != true {
t.Errorf("allow_all = %v, want true", m["allow_all"])
}
})
t.Run("raw + scope-all -> error", func(t *testing.T) {
if _, err := buildRequestScope(true, nil, `{"allow_all":true}`); err == nil {
t.Errorf("raw + scope-all must error")
}
})
t.Run("raw + scope-api -> error", func(t *testing.T) {
if _, err := buildRequestScope(false, []string{"GET /openapi/x"}, `{"allow_all":true}`); err == nil {
t.Errorf("raw + scope-api must error")
}
})
t.Run("invalid raw json -> error", func(t *testing.T) {
if _, err := buildRequestScope(false, nil, "{bad"); err == nil {
t.Errorf("invalid json must error")
}
})
}
func TestBuildKeyConfig(t *testing.T) {
t.Run("nothing set -> nil", func(t *testing.T) {
cfg, err := buildKeyConfig(false, nil, "", false, false)
if err != nil || cfg != nil {
t.Fatalf("empty -> nil, got cfg=%v err=%v", cfg, err)
}
})
t.Run("scope-all -> snake_case request_scope", func(t *testing.T) {
cfg, err := buildKeyConfig(true, nil, "", false, false)
if err != nil {
t.Fatalf("err = %v", err)
}
rs := cfg["request_scope"].(map[string]interface{})
if rs["allow_all"] != true {
t.Errorf("allow_all = %v, want true", rs["allow_all"])
}
if _, ok := cfg["is_allow_access_preview"]; ok {
t.Errorf("is_allow_access_preview should not appear")
}
})
t.Run("scope-api -> snake_case http_infos", func(t *testing.T) {
cfg, err := buildKeyConfig(false, []string{"GET /openapi/x"}, "", false, false)
if err != nil {
t.Fatalf("err = %v", err)
}
rs := cfg["request_scope"].(map[string]interface{})
if rs["allow_all"] != false {
t.Errorf("allow_all = %v, want false", rs["allow_all"])
}
infos := rs["http_infos"].([]interface{})
if len(infos) != 1 {
t.Fatalf("http_infos len = %d, want 1", len(infos))
}
info := infos[0].(map[string]interface{})
if info["http_method"] != "GET" || info["http_path"] != "/openapi/x" {
t.Errorf("info = %v", info)
}
})
t.Run("raw scope passthrough", func(t *testing.T) {
cfg, err := buildKeyConfig(false, nil, `{"allow_all":true}`, false, false)
if err != nil {
t.Fatalf("err = %v", err)
}
rs := cfg["request_scope"].(map[string]interface{})
if rs["allow_all"] != true {
t.Errorf("allow_all = %v", rs["allow_all"])
}
})
t.Run("allow-preview only -> is_allow_access_preview", func(t *testing.T) {
cfg, err := buildKeyConfig(false, nil, "", true, true)
if err != nil {
t.Fatalf("err = %v", err)
}
if _, ok := cfg["request_scope"]; ok {
t.Errorf("request_scope should not appear when not set")
}
if cfg["is_allow_access_preview"] != true {
t.Errorf("is_allow_access_preview = %v, want true", cfg["is_allow_access_preview"])
}
})
t.Run("scope-all + allow-preview -> both snake_case keys", func(t *testing.T) {
cfg, err := buildKeyConfig(true, nil, "", true, false)
if err != nil {
t.Fatalf("err = %v", err)
}
if _, ok := cfg["request_scope"]; !ok {
t.Errorf("request_scope missing")
}
if cfg["is_allow_access_preview"] != false {
t.Errorf("is_allow_access_preview = %v, want false", cfg["is_allow_access_preview"])
}
// ensure no camelCase keys
if _, ok := cfg["requestScope"]; ok {
t.Errorf("found camelCase key requestScope — must use snake_case")
}
if _, ok := cfg["isAllowAccessPreview"]; ok {
t.Errorf("found camelCase key isAllowAccessPreview — must use snake_case")
}
})
t.Run("raw + scope-all -> error", func(t *testing.T) {
if _, err := buildKeyConfig(true, nil, `{"allow_all":true}`, false, false); err == nil {
t.Errorf("raw + scope-all must error")
}
})
t.Run("invalid json -> error", func(t *testing.T) {
if _, err := buildKeyConfig(false, nil, "{bad", false, false); err == nil {
t.Errorf("invalid json must error")
}
})
t.Run("no camelCase keys emitted", func(t *testing.T) {
cfg, err := buildKeyConfig(false, []string{"GET /openapi/x"}, "", true, true)
if err != nil {
t.Fatalf("err = %v", err)
}
if _, ok := cfg["requestScope"]; ok {
t.Errorf("camelCase requestScope must not appear")
}
if _, ok := cfg["isAllowAccessPreview"]; ok {
t.Errorf("camelCase isAllowAccessPreview must not appear")
}
rs := cfg["request_scope"].(map[string]interface{})
infos := rs["http_infos"].([]interface{})
info := infos[0].(map[string]interface{})
wantInfo := map[string]interface{}{"http_method": "GET", "http_path": "/openapi/x"}
if !reflect.DeepEqual(info, wantInfo) {
t.Errorf("info = %v, want %v", info, wantInfo)
}
})
}