|
|
@@ -0,0 +1,565 @@
|
|
|
+// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
+// SPDX-License-Identifier: BSD-3-Clause
|
|
|
+
|
|
|
+package setting
|
|
|
+
|
|
|
+import (
|
|
|
+ "reflect"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ jsonv2 "github.com/go-json-experiment/json"
|
|
|
+)
|
|
|
+
|
|
|
+func TestPolicyScopeIsApplicableSetting(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ scope PolicyScope
|
|
|
+ setting *Definition
|
|
|
+ wantApplicable bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "DeviceScope/DeviceSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/ProfileSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantApplicable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/UserSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantApplicable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/DeviceSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/ProfileSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/UserSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantApplicable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/DeviceSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/ProfileSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/UserSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantApplicable: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ gotApplicable := tt.scope.IsApplicableSetting(tt.setting)
|
|
|
+ if gotApplicable != tt.wantApplicable {
|
|
|
+ t.Fatalf("got %v, want %v", gotApplicable, tt.wantApplicable)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestPolicyScopeIsConfigurableSetting(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ scope PolicyScope
|
|
|
+ setting *Definition
|
|
|
+ wantConfigurable bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "DeviceScope/DeviceSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/ProfileSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/UserSetting",
|
|
|
+ scope: DeviceScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/DeviceSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantConfigurable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/ProfileSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/UserSetting",
|
|
|
+ scope: CurrentProfileScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/DeviceSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", DeviceSetting, IntegerValue),
|
|
|
+ wantConfigurable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/ProfileSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", ProfileSetting, IntegerValue),
|
|
|
+ wantConfigurable: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/UserSetting",
|
|
|
+ scope: CurrentUserScope,
|
|
|
+ setting: NewDefinition("TestSetting", UserSetting, IntegerValue),
|
|
|
+ wantConfigurable: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ gotConfigurable := tt.scope.IsConfigurableSetting(tt.setting)
|
|
|
+ if gotConfigurable != tt.wantConfigurable {
|
|
|
+ t.Fatalf("got %v, want %v", gotConfigurable, tt.wantConfigurable)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestPolicyScopeContains(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ scopeA PolicyScope
|
|
|
+ scopeB PolicyScope
|
|
|
+ wantAContainsB bool
|
|
|
+ wantAStrictlyContainsB bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "DeviceScope/DeviceScope",
|
|
|
+ scopeA: DeviceScope,
|
|
|
+ scopeB: DeviceScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/CurrentProfileScope",
|
|
|
+ scopeA: DeviceScope,
|
|
|
+ scopeB: CurrentProfileScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "DeviceScope/UserScope",
|
|
|
+ scopeA: DeviceScope,
|
|
|
+ scopeB: CurrentUserScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/DeviceScope",
|
|
|
+ scopeA: CurrentProfileScope,
|
|
|
+ scopeB: DeviceScope,
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/ProfileScope",
|
|
|
+ scopeA: CurrentProfileScope,
|
|
|
+ scopeB: CurrentProfileScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope/UserScope",
|
|
|
+ scopeA: CurrentProfileScope,
|
|
|
+ scopeB: CurrentUserScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/DeviceScope",
|
|
|
+ scopeA: CurrentUserScope,
|
|
|
+ scopeB: DeviceScope,
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/ProfileScope",
|
|
|
+ scopeA: CurrentUserScope,
|
|
|
+ scopeB: CurrentProfileScope,
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope/UserScope",
|
|
|
+ scopeA: CurrentUserScope,
|
|
|
+ scopeB: CurrentUserScope,
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope(1234)/UserScope(1234)",
|
|
|
+ scopeA: UserScopeOf("1234"),
|
|
|
+ scopeB: UserScopeOf("1234"),
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope(1234)/UserScope(5678)",
|
|
|
+ scopeA: UserScopeOf("1234"),
|
|
|
+ scopeB: UserScopeOf("5678"),
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope(A)/UserScope(A/1234)",
|
|
|
+ scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
|
|
|
+ scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "ProfileScope(A)/UserScope(B/1234)",
|
|
|
+ scopeA: PolicyScope{kind: ProfileSetting, profileID: "A"},
|
|
|
+ scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "B"},
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope(1234)/UserScope(A/1234)",
|
|
|
+ scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
|
|
|
+ scopeB: PolicyScope{kind: UserSetting, userID: "1234", profileID: "A"},
|
|
|
+ wantAContainsB: true,
|
|
|
+ wantAStrictlyContainsB: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "UserScope(1234)/UserScope(A/5678)",
|
|
|
+ scopeA: PolicyScope{kind: UserSetting, userID: "1234"},
|
|
|
+ scopeB: PolicyScope{kind: UserSetting, userID: "5678", profileID: "A"},
|
|
|
+ wantAContainsB: false,
|
|
|
+ wantAStrictlyContainsB: false,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ gotContains := tt.scopeA.Contains(tt.scopeB)
|
|
|
+ if gotContains != tt.wantAContainsB {
|
|
|
+ t.Fatalf("WithinOf: got %v, want %v", gotContains, tt.wantAContainsB)
|
|
|
+ }
|
|
|
+
|
|
|
+ gotStrictlyContains := tt.scopeA.StrictlyContains(tt.scopeB)
|
|
|
+ if gotStrictlyContains != tt.wantAStrictlyContainsB {
|
|
|
+ t.Fatalf("StrictlyWithinOf: got %v, want %v", gotStrictlyContains, tt.wantAStrictlyContainsB)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestPolicyScopeMarshalUnmarshal(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ in any
|
|
|
+ wantJSON string
|
|
|
+ wantError bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "null-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{},
|
|
|
+ wantJSON: `{"Scope":"Device"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "null-scope-omit-zero",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope `json:",omitzero"`
|
|
|
+ }{},
|
|
|
+ wantJSON: `{}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "device-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{DeviceScope},
|
|
|
+ wantJSON: `{"Scope":"Device"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "current-profile-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{CurrentProfileScope},
|
|
|
+ wantJSON: `{"Scope":"Profile"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "current-user-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{CurrentUserScope},
|
|
|
+ wantJSON: `{"Scope":"User"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "specific-user-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{UserScopeOf("_")},
|
|
|
+ wantJSON: `{"Scope":"User(_)"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "specific-user-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{UserScopeOf("S-1-5-21-3698941153-1525015703-2649197413-1001")},
|
|
|
+ wantJSON: `{"Scope":"User(S-1-5-21-3698941153-1525015703-2649197413-1001)"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "specific-profile-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{PolicyScope{kind: ProfileSetting, profileID: "1234"}},
|
|
|
+ wantJSON: `{"Scope":"Profile(1234)"}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "specific-profile-and-user-scope",
|
|
|
+ in: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{PolicyScope{
|
|
|
+ kind: UserSetting,
|
|
|
+ profileID: "1234",
|
|
|
+ userID: "S-1-5-21-3698941153-1525015703-2649197413-1001",
|
|
|
+ }},
|
|
|
+ wantJSON: `{"Scope":"Profile(1234)/User(S-1-5-21-3698941153-1525015703-2649197413-1001)"}`,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ gotJSON, err := jsonv2.Marshal(tt.in)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("Marshal failed: %v", err)
|
|
|
+ }
|
|
|
+ if string(gotJSON) != tt.wantJSON {
|
|
|
+ t.Fatalf("Marshal got %s, want %s", gotJSON, tt.wantJSON)
|
|
|
+ }
|
|
|
+ wantBack := tt.in
|
|
|
+ gotBack := reflect.New(reflect.TypeOf(tt.in).Elem()).Interface()
|
|
|
+ err = jsonv2.Unmarshal(gotJSON, gotBack)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("Unmarshal failed: %v", err)
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(gotBack, wantBack) {
|
|
|
+ t.Fatalf("Unmarshal got %+v, want %+v", gotBack, wantBack)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestPolicyScopeUnmarshalSpecial(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ json string
|
|
|
+ want any
|
|
|
+ wantError bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "empty",
|
|
|
+ json: "{}",
|
|
|
+ want: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "too-many-scopes",
|
|
|
+ json: `{"Scope":"Device/Profile/User"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "user/profile", // incorrect order
|
|
|
+ json: `{"Scope":"User/Profile"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "profile-user-no-params",
|
|
|
+ json: `{"Scope":"Profile/User"}`,
|
|
|
+ want: &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{CurrentUserScope},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "unknown-scope",
|
|
|
+ json: `{"Scope":"Unknown"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "unknown-scope/unknown-scope",
|
|
|
+ json: `{"Scope":"Unknown/Unknown"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "device-scope/unknown-scope",
|
|
|
+ json: `{"Scope":"Device/Unknown"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "unknown-scope/device-scope",
|
|
|
+ json: `{"Scope":"Unknown/Device"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "slash",
|
|
|
+ json: `{"Scope":"/"}`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "empty",
|
|
|
+ json: `{"Scope": ""`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "no-closing-bracket",
|
|
|
+ json: `{"Scope": "user(1234"`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "device-with-id",
|
|
|
+ json: `{"Scope": "device(123)"`,
|
|
|
+ wantError: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ got := &struct {
|
|
|
+ Scope PolicyScope
|
|
|
+ }{}
|
|
|
+ err := jsonv2.Unmarshal([]byte(tt.json), got)
|
|
|
+ if (err != nil) != tt.wantError {
|
|
|
+ t.Errorf("Marshal error: got %v, want %v", err, tt.wantError)
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(got, tt.want) {
|
|
|
+ t.Fatalf("Unmarshal got %+v, want %+v", got, tt.want)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func TestExtractScopeAndParams(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ s string
|
|
|
+ scope string
|
|
|
+ params string
|
|
|
+ wantOk bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "empty",
|
|
|
+ s: "",
|
|
|
+ wantOk: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "scope-only",
|
|
|
+ s: "device",
|
|
|
+ scope: "device",
|
|
|
+ wantOk: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "scope-with-params",
|
|
|
+ s: "user(1234)",
|
|
|
+ scope: "user",
|
|
|
+ params: "1234",
|
|
|
+ wantOk: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "params-empty-scope",
|
|
|
+ s: "(1234)",
|
|
|
+ scope: "",
|
|
|
+ params: "1234",
|
|
|
+ wantOk: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "params-with-brackets",
|
|
|
+ s: "test()())))())",
|
|
|
+ scope: "test",
|
|
|
+ params: ")())))()",
|
|
|
+ wantOk: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "no-closing-bracket",
|
|
|
+ s: "user(1234",
|
|
|
+ scope: "",
|
|
|
+ params: "",
|
|
|
+ wantOk: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "open-before-close",
|
|
|
+ s: ")user(1234",
|
|
|
+ scope: "",
|
|
|
+ params: "",
|
|
|
+ wantOk: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "brackets-only",
|
|
|
+ s: ")(",
|
|
|
+ scope: "",
|
|
|
+ params: "",
|
|
|
+ wantOk: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "closing-bracket",
|
|
|
+ s: ")",
|
|
|
+ scope: "",
|
|
|
+ params: "",
|
|
|
+ wantOk: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "opening-bracket",
|
|
|
+ s: ")",
|
|
|
+ scope: "",
|
|
|
+ params: "",
|
|
|
+ wantOk: false,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ scope, params, ok := extractScopeAndParams(tt.s)
|
|
|
+ if ok != tt.wantOk {
|
|
|
+ t.Logf("OK: got %v; want %v", ok, tt.wantOk)
|
|
|
+ }
|
|
|
+ if scope != tt.scope {
|
|
|
+ t.Logf("Scope: got %q; want %q", scope, tt.scope)
|
|
|
+ }
|
|
|
+ if params != tt.params {
|
|
|
+ t.Logf("Params: got %v; want %v", params, tt.params)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|